//#region 定义变量

//服务端基包名称
basePackageName = "com.xci"
//客户端命名空间
clientNamespace = "XCI.Win"
//模块名称
moduleName = "basic"
//作者
author = "吕艳阳"
//数据类型映射
typeMapping = [
        (~/(?i)bigint/)                          : "Long",
        (~/(?i)int|integer|tinyint/)             : "Integer",
        (~/(?i)bit/)                             : "Boolean",
        (~/(?i)float|double|decimal|real|number/): "Double",
        (~/(?i)datetime|date|timestamp|time/)    : "Date",
        (~/(?i)/)                                : "String"
]

//#endregion

//#region 导入包

import com.intellij.database.model.DasColumn
import com.intellij.database.model.DasTable
import com.intellij.database.model.ObjectKind
import com.intellij.database.util.Case
import com.intellij.database.util.DasUtil
import com.intellij.psi.codeStyle.NameUtil

//#endregion

//#region 选择目录

FILES.chooseDirectoryAndSave("选择目录", "生成的文件将保存到指定目录") {
    dir -> SELECTION.filter { it instanceof DasTable && it.getKind() == ObjectKind.TABLE }.each { generate(it, dir) }
}

//#endregion

//#region 生成总控

def generate(table, dir) {
    DbTable t = getDbTable(table)

    //服务端
    //region dao
    File daoDir = new File(dir, "build/server/dao")
    !daoDir.exists() && daoDir.mkdirs()
    new File(daoDir.getAbsolutePath(), t.className + "Dao.java").withPrintWriter("utf-8") { out -> generateDao(out, t) }
    //endregion
    //region mapper
    File mapperDir = new File(dir, "/build/server/mapper")
    !mapperDir.exists() && mapperDir.mkdirs()
    new File(mapperDir.getAbsolutePath(), t.className + "Dao.xml").withPrintWriter("utf-8") { out -> generateMapper(out, t) }
    //endregion
    //region service
    File serviceDir = new File(dir, "/build/server/service")
    !serviceDir.exists() && serviceDir.mkdirs()
    new File(serviceDir.getAbsolutePath(), t.className + "Service.java").withPrintWriter("utf-8") { out -> generateService(out, t) }
    //endregion
    //region apiController
    File apiControllerDir = new File(dir, "/build/server/api")
    !apiControllerDir.exists() && apiControllerDir.mkdirs()
    new File(apiControllerDir.getAbsolutePath(), t.className + "ApiController.java").withPrintWriter("utf-8") { out -> generateApiController(out, t) }
    //endregion
    //region entity
    File entityDir = new File(dir, "/build/server/entity")
    !entityDir.exists() && entityDir.mkdirs()
    new File(entityDir.getAbsolutePath(), t.className + ".java").withPrintWriter("utf-8") { out -> generateEntity(out, t) }
    //endregion
    //region filter
    File filterDir = new File(dir, "/build/server/filter")
    !filterDir.exists() && filterDir.mkdirs()
    new File(filterDir.getAbsolutePath(), t.className + "Filter.java").withPrintWriter("utf-8") { out -> generateFilter(out, t) }
    //endregion

    //客户端
    //region Service
    File clientServiceDir = new File(dir, "/build/client/Service")
    !clientServiceDir.exists() && clientServiceDir.mkdirs()
    new File(clientServiceDir.getAbsolutePath(), t.className + "Service.cs").withPrintWriter("utf-8") { out -> generateClientService(out, t) }
    //endregion
    //region Filter
    File clientFilterDir = new File(dir, "/build/client/Filter")
    !clientFilterDir.exists() && clientFilterDir.mkdirs()
    new File(clientFilterDir.getAbsolutePath(), t.className + "Filter.cs").withPrintWriter("utf-8") { out -> generateClientFilter(out, t) }
    //endregion
    //region Entity
    File clientEntityDir = new File(dir, "/build/client/Entity")
    !clientEntityDir.exists() && clientEntityDir.mkdirs()
    new File(clientEntityDir.getAbsolutePath(), t.className + ".cs").withPrintWriter("utf-8") { out -> generateClientEntity(out, t) }
    //endregion
    //region Form
    File clientFormDir = new File(dir, "/build/client/Forms/" + t.className)
    !clientFormDir.exists() && clientFormDir.mkdirs()

    new File(clientFormDir.getAbsolutePath(), "Frm" + t.className + "Manager.cs").withPrintWriter("utf-8") { out -> generateClientManagerForm(out, t) }
    new File(clientFormDir.getAbsolutePath(), "Frm" + t.className + "Manager.designer.cs").withPrintWriter("utf-8") { out -> generateClientManagerDesignerForm(out, t) }
    new File(clientFormDir.getAbsolutePath(), "Frm" + t.className + "Edit.cs").withPrintWriter("utf-8") { out -> generateClientEditForm(out, t) }
    new File(clientFormDir.getAbsolutePath(), "Frm" + t.className + "Edit.designer.cs").withPrintWriter("utf-8") { out -> generateClientEditDesignerForm(out, t) }
    new File(clientFormDir.getAbsolutePath(), "Frm" + t.className + "Details.cs").withPrintWriter("utf-8") { out -> generateClientDetailsForm(out, t) }
    new File(clientFormDir.getAbsolutePath(), "Frm" + t.className + "Details.designer.cs").withPrintWriter("utf-8") { out -> generateClientDetailsDesignerForm(out, t) }
    //endregion
}

//#endregion

//#region 生成服务端代码

def generateEntity(Writer out, DbTable t) {
    out.println "package ${basePackageName}.entity;"
    out.println ""
    out.println "import cn.afterturn.easypoi.excel.annotation.Excel;"
    out.println "import cn.afterturn.easypoi.excel.annotation.ExcelIgnore;"
    out.println "import com.xci.core.domain.BaseEntity;"
    out.println "import com.xci.core.internal.Const;"
    out.println "import io.swagger.annotations.ApiModel;"
    out.println "import io.swagger.annotations.ApiModelProperty;"
    out.println "import lombok.Data;"
    out.println "import lombok.EqualsAndHashCode;"
    out.println "import org.hibernate.validator.constraints.Length;"
    out.println ""
    out.println "import javax.validation.constraints.NotBlank;"
    out.println "import javax.validation.constraints.NotNull;"
    out.println "import java.io.Serializable;"
    out.println "import java.util.Date;"

    out.println "/**"
    out.println " * ${t.comment}"
    out.println " * @author ${author}"
    out.println " */"
    out.println "@Data @EqualsAndHashCode(callSuper = true)"
    out.println "@ApiModel(description = \"${t.comment}\")"
    out.println "public class ${t.className} extends BaseEntity implements Serializable {"
    out.println getSerialId()
    out.println ""
    def index = 1
    t.fields.each() {
        def colComment = clearColumnOptions(it.comment)
        out.println "\t/**"
        out.println "\t * ${it.comment}"
        out.println "\t */"
        //#region 验证
        if (it.type == "String" || isIdColumn(it)) {
            if (it.dasColumn.isNotNull()) {
                out.println "\t@NotBlank(message = \"请输入${colComment}\")"
            }
            def len = it.size
            if (len < 0) {
                if (it.isKey) {
                    len = 20
                } else {
                    len = 100
                }
            }
            out.println "\t@Length(max = ${len}, message = \"${colComment}长度不能超过{max}\")";
        } else {
            if (it.dasColumn.isNotNull()) {
                out.println "\t@NotNull(message = \"请输入${colComment}\")"
            }
        }
        //#endregion

        //#region @Excel
        if (isIdColumn(it)) {
            out.println "\t@ExcelIgnore"
        }

        if (it.type == "Date") {
            out.println "\t@Excel(name = \"${colComment}\", width = 20d, exportFormat = Const.DEFAULT_DATETIME_PATTERN)"
        } else if (hasColumnOptions(it.comment)) {
            out.println "\t@Excel(name = \"${colComment}\", width = 20d, replace = { ${getExcelReplace(it.comment)} })"
        } else {
            out.println "\t@Excel(name = \"${colComment}\", width = 20d)"
        }
        //#endregion

        //#region @ApiModelProperty
        if (!it.nullable) {
            out.println "\t@ApiModelProperty(value = \"${it.comment}\", required = true, position = ${index})"
        } else {
            out.println "\t@ApiModelProperty(value = \"${it.comment}\", position = ${index})"
        }
        //#endregion
        out.println "\tprivate ${getModelFieldType(it)} ${it.javaName};"
        if (t.fields.size() != index) out.println ""
        index++
    }
    out.println "}"
}

def generateFilter(Writer out, DbTable t) {
    out.println "package ${basePackageName}.filter;"
    out.println ""
    out.println "import com.xci.core.domain.BaseFilter;"
    out.println "import io.swagger.annotations.ApiModel;"
    out.println "import io.swagger.annotations.ApiModelProperty;"
    out.println "import lombok.Data;"
    out.println "import lombok.EqualsAndHashCode;"
    out.println ""
    out.println "import java.util.Date;"
    out.println ""

    out.println "/**"
    out.println " * ${t.comment}"
    out.println " * @author ${author}"
    out.println " */"
    out.println "@Data @EqualsAndHashCode(callSuper = true)"
    out.println "@ApiModel(description = \"${t.comment}\")"
    out.println "public class ${t.className}Filter extends BaseFilter{"
    def index = 1
    t.fields.each() {
        out.println "\t/**"
        out.println "\t * ${it.comment}"
        out.println "\t */"
        out.println "\t@ApiModelProperty(value = \"${it.comment}\", position = ${index})"
        out.println "\tprivate ${getModelFieldType(it)} ${it.javaName};"
        if (t.fields.size() != index) out.println ""
        index++
    }
    out.println "}"
}

def generateApiController(Writer out, DbTable t) {
    def tFieldName = objectName(t.name, false);
    def serviceObjStr = tFieldName + "Service"
    out.println "package ${basePackageName}.api;"
    out.println ""
    out.println "import com.xci.core.annotation.Authorize;"
    out.println "import com.xci.core.base.PageList;"
    out.println "import com.xci.core.base.RestMessage;"
    out.println "import com.xci.core.domain.PageModel;"
    out.println "import com.xci.core.helper.ExcelHelper;"
    out.println "import com.xci.core.internal.Const;"
    out.println "import com.xci.core.base.ApiController;"
    out.println "import ${basePackageName}.entity.${t.className};"
    out.println "import ${basePackageName}.filter.${t.className}Filter;"
    out.println "import ${basePackageName}.service.${t.className}Service;"
    out.println "import io.swagger.annotations.*;"
    out.println "import org.springframework.web.bind.annotation.*;"
    out.println ""
    out.println "import javax.annotation.Resource;"
    out.println "import java.util.List;"
    out.println ""
    out.println "/**"
    out.println " * ${t.comment}接口"
    out.println " * @author ${author}"
    out.println " */"
    out.println "@Authorize"
    out.println "@RestController"
    out.println "@Api(tags = \"${t.comment}接口\")"
    out.println "@RequestMapping(value = \"/api/${moduleName}/${tFieldName}\", produces = Const.P_JSON)"
    out.println "public class ${t.className}ApiController extends ApiController {"
    out.println "    /**"
    out.println "     * ${t.comment}服务"
    out.println "     */"
    out.println "    @Resource"
    out.println "    private ${t.className}Service ${serviceObjStr};"
    out.println ""
    out.println "    //@ApiOperationSupport(order = 1)"
    out.println "    //@ApiOperation(value = \"检测${t.comment}主键是否存在\")"
    out.println "    //@ApiImplicitParam(name = \"id\", value = \"主键\", required = true)"
    out.println "    //@PostMapping(\"/existById/{id}\")"
    out.println "    //public RestMessage existById(@PathVariable String id) {"
    out.println "    //    return new BoolMessage(${serviceObjStr}.existById(id));"
    out.println "    //}"
    out.println ""
    out.println "    //@ApiOperationSupport(order = 2)"
    out.println "    //@ApiOperation(value = \"检测${t.comment}名称是否存在\")"
    out.println "    //@ApiImplicitParams({"
    out.println "    //    @ApiImplicitParam(name = \"code\", value = \"名称\", required = true),"
    out.println "    //    @ApiImplicitParam(name = \"id\", value = \"排除的主键(可选)\")"
    out.println "    //})"
    out.println "    //@PostMapping(\"/existByName/{name}/{id}\")"
    out.println "    //public BoolMessage existByName(@PathVariable String name, @PathVariable String id) {"
    out.println "    //    return new BoolMessage(${serviceObjStr}.existByName(name, id));"
    out.println "    //}"
    out.println ""
    out.println "    //@ApiOperationSupport(order = 3)"
    out.println "    //@ApiOperation(value = \"检查${t.comment}编码是否存在\")"
    out.println "    //@ApiImplicitParams({"
    out.println "    //    @ApiImplicitParam(name = \"code\", value = \"编码\", required = true),"
    out.println "    //    @ApiImplicitParam(name = \"id\", value = \"排除的主键(可选)\")"
    out.println "    //})"
    out.println "    //@PostMapping(\"/existByCode/{code}/{id}\")"
    out.println "    //public BoolMessage existByCode(@PathVariable String code, @PathVariable String id) {"
    out.println "    //    return new BoolMessage(${serviceObjStr}.existByCode(code, id));"
    out.println "    //}"
    out.println ""
    out.println "    @ApiOperationSupport(order = 4)"
    out.println "    @ApiOperation(value = \"新建${t.comment}\")"
    out.println "    //@Authorize(code = \"${moduleName}.${tFieldName}.insert\")"
    out.println "    @PostMapping(\"/insert\")"
    out.println "    public RestMessage insert(@RequestBody ${t.className} entity) {"
    out.println "        return ${serviceObjStr}.insert(entity);"
    out.println "    }"
    out.println ""
    out.println "    @ApiOperationSupport(order = 5)"
    out.println "    @ApiOperation(value = \"修改${t.comment}\")"
    out.println "    //@Authorize(code = \"${moduleName}.${tFieldName}.update\")"
    out.println "    @PostMapping(\"/update\")"
    out.println "    public RestMessage update(@RequestBody ${t.className} entity) {"
    out.println "        return ${serviceObjStr}.update(entity);"
    out.println "    }"
    out.println ""
    out.println "    @ApiOperationSupport(order = 6)"
    out.println "    @ApiOperation(value = \"根据主键字符串删除${t.comment}\")"
    out.println "    @ApiImplicitParam(name = \"ids\", value = \"主键字符串,多个逗号隔开\", required = true)"
    out.println "    //@Authorize(code = \"${moduleName}.${tFieldName}.delete\")"
    out.println "    @PostMapping(\"/delete\")"
    out.println "    public RestMessage delete(String ids) {"
    out.println "        return ${serviceObjStr}.delete(ids);"
    out.println "    }"
    out.println ""
    out.println "    @ApiOperationSupport(order = 7)"
    out.println "    @ApiOperation(value = \"根据主键查询单个${t.comment}\")"
    out.println "    @ApiImplicitParam(name = \"id\", value = \"${t.comment}主键\", required = true)"
    out.println "    //@Authorize(code = \"${moduleName}.${tFieldName}.select\")"
    out.println "    @PostMapping(\"/selectById/{id}\")"
    out.println "    public BoolMessage<${t.className}> selectById(@PathVariable String id) {"
    out.println "        return BoolMessage.success(${serviceObjStr}.selectById(id));"
    out.println "    }"
    out.println ""
    out.println "    //@ApiOperationSupport(order = 8)"
    out.println "    //@ApiOperation(value = \"根据编码查询单个${t.comment}\")"
    out.println "    //@ApiImplicitParam(name = \"code\", value = \"${t.comment}编码\", required = true)"
    out.println "    ////@Authorize(code = \"${moduleName}.${tFieldName}.select\")"
    out.println "    //@PostMapping(\"/selectByCode/{code}\")"
    out.println "    //public RestMessage<${t.className}> selectByCode(@PathVariable String code) {"
    out.println "    //    return RestMessage.success(${serviceObjStr}.selectByCode(code));"
    out.println "    //}"
    out.println ""
    out.println "    @ApiOperationSupport(order = 9)"
    out.println "    @ApiOperation(value = \"查询${t.comment}列表\")"
    out.println "    //@Authorize(code = \"${moduleName}.${tFieldName}.select\")"
    out.println "    @PostMapping(\"/selectList\")"
    out.println "    public RestMessage<List<${t.className}>> selectList(${t.className}Filter filter) {"
    out.println "        return RestMessage.success(${serviceObjStr}.selectList(filter));"
    out.println "    }"
    out.println ""
    out.println "    @ApiOperationSupport(order = 10)"
    out.println "    @ApiOperation(value = \"查询${t.comment}分页列表\")"
    out.println "    //@Authorize(code = \"${moduleName}.${tFieldName}.select\")"
    out.println "    @PostMapping(\"/selectPageList\")"
    out.println "    public RestMessage<PageList<${t.className}>> selectPageList(PageModel pageModel, ${t.className}Filter filter) {"
    out.println "        return RestMessage.success(PageList.of(${serviceObjStr}.selectPageList(filter)));"
    out.println "    }"
    out.println ""
    out.println "    @ApiOperationSupport(order = 11)"
    out.println "    @ApiOperation(value = \"导出${t.comment}列表\")"
    out.println "    //@Authorize(code = \"${moduleName}.${tFieldName}.export\")"
    out.println "    @PostMapping(value = \"/export\", produces = {Const.P_OCTET, Const.P_JSON})"
    out.println "    public void export(${t.className}Filter filter) {"
    out.println "        ExcelHelper.exportWeb(${serviceObjStr}.selectList(filter), ${t.className}.class, \"${t.comment}列表\");"
    out.println "    }"
    out.println "}"
}

def generateService(Writer out, DbTable t) {
    def daoObjStr = objectName(t.name, false) + "Dao"
    out.println "package ${basePackageName}.service;"
    out.println ""
    out.println "import com.github.pagehelper.Page;"
    out.println "import com.xci.core.base.RestMessage;"
    out.println "import com.xci.core.exceptions.AppException;"
    out.println "import com.xci.core.helper.Helper;"
    out.println "import com.xci.core.annotation.Log;"
    out.println "import com.xci.core.base.BaseService;"
    out.println "import com.xci.core.domain.HistoryInfo;"
    out.println "import com.xci.core.internal.Const;"
    out.println "import ${basePackageName}.dao.${t.className}Dao;"
    out.println "import ${basePackageName}.entity.${t.className};"
    out.println "import ${basePackageName}.filter.${t.className}Filter;"
    out.println "import lombok.extern.slf4j.Slf4j;"
    out.println "import org.springframework.stereotype.Service;"
    out.println "import org.springframework.transaction.annotation.Transactional;"
    out.println "import org.springframework.validation.annotation.Validated;"
    out.println ""
    out.println "import javax.annotation.Resource;"
    out.println "import javax.validation.constraints.NotBlank;"
    out.println "import java.util.List;"
    out.println ""
    out.println "/**"
    out.println " * ${t.comment}服务"
    out.println " * @author ${author}"
    out.println " */"
    out.println "@Slf4j"
    out.println "@Service"
    out.println "public class ${t.className}Service extends BaseService {"
    out.println "    /**"
    out.println "     * ${t.comment}数据层对象"
    out.println "     */"
    out.println "    @Resource"
    out.println "    private ${t.className}Dao ${daoObjStr};"
    out.println ""
    out.println "    ///**"
    out.println "    // * 检测${t.comment}主键是否存在"
    out.println "    // * @param id 主键"
    out.println "    // * @return 如果存在返回true, 否则返回false"
    out.println "    // */"
    out.println "    //"
    out.println "    //public boolean existById(@NotBlank(message = \"请指定${t.comment}主键\") String id) {"
    out.println "    //    return ${daoObjStr}.existById(id);"
    out.println "    //}"
    out.println ""
    out.println "    ///**"
    out.println "    // * 检测${t.comment}名称是否存在"
    out.println "    // * @param name 名称"
    out.println "    // * @param id 排除的主键"
    out.println "    // * @return 如果存在返回true, 否则返回false"
    out.println "    // */"
    out.println "    //"
    out.println "    //public boolean existByName(@NotBlank(message = \"请指定${t.comment}名称\") String name, String id) {"
    out.println "    //    return ${daoObjStr}.existByName(name, id);"
    out.println "    //}"
    out.println ""
    out.println "    ///**"
    out.println "    // * 检测${t.comment}编码是否存在"
    out.println "    // * @param code 编码"
    out.println "    // * @param id 排除的主键"
    out.println "    // * @return 如果存在返回true, 否则返回false"
    out.println "    // */"
    out.println "    //"
    out.println "    //public boolean existByCode(@NotBlank(message = \"请指定${t.comment}编码\") String code, String id) {"
    out.println "    //    return ${daoObjStr}.existByCode(code, id);"
    out.println "    //}"
    out.println ""
    out.println "    /**"
    out.println "     * 预处理${t.comment}对象"
    out.println "     * @param entity 数据对象"
    out.println "     * @param created 是否新建"
    out.println "     */"
    out.println "    private RestMessage checkEntity(${t.className} entity, boolean created) {"
    out.println "        ////名称简拼"
    out.println "        //if(Helper.isBlank(entity.getSpell())) {"
    out.println "        //    entity.setSpell(Sh.getSpell(entity.getName()));"
    out.println "        //}"
    out.println ""
    out.println "        ////检测名称是否存在"
    out.println "        //if(${daoObjStr}.existByName(entity.getName(), created ? null : entity.getId())) {"
    out.println "        //    String msg = Helper.format(\"${t.comment}名称 {} 已经存在\", entity.getName());"
    out.println "        //    return BoolMessage.fail(msg);"
    out.println "        //}"
    out.println "        ////检测编码是否存在"
    out.println "        //if(${daoObjStr}.existByCode(entity.getCode(), created ? null : entity.getId())) {"
    out.println "        //    String msg = Helper.format(\"${t.comment}编码 {} 已经存在\", entity.getCode());"
    out.println "        //    return BoolMessage.fail(msg);"
    out.println "        //}"
    out.println "        return RestMessage.success();"
    out.println "    }"
    out.println ""
    out.println "    /**"
    out.println "     * 新建${t.comment}"
    out.println "     * @param entity 数据对象"
    out.println "     * @return 执行成功返回success, 否则返回fail"
    out.println "     */"
    out.println "    @Log(module = \"${moduleName}\", msg = \"新建${t.comment}\")"
    out.println "    @Transactional(rollbackFor = Exception.class)"
    out.println "    public RestMessage insert(${t.className} entity) {"
    out.println "        RestMessage checkResult = checkEntity(entity, true);"
    out.println "        if(checkResult.isFail()) return checkResult;"
    out.println ""
    out.println "        ${daoObjStr}.insert(entity);"
    out.println "        saveInsertHistory(HistoryInfo.builder().pk(entity.get${objectName(t.keyName,true)}()).after(entity).build());"
    out.println "        return RestMessage.success();"
    out.println "    }"
    out.println ""
    out.println "    /**"
    out.println "     * 修改${t.comment}"
    out.println "     * @param entity 数据对象"
    out.println "     * @return 执行成功返回success, 否则返回fail"
    out.println "     */"
    out.println "    @Transactional(rollbackFor = Exception.class)"
    out.println "    @Log(module = \"${moduleName}\", msg = \"修改${t.comment}\")"
    out.println "    public RestMessage update(${t.className} entity) {"
    out.println "        RestMessage checkResult = checkEntity(entity, false);"
    out.println "        if(checkResult.isFail()) return checkResult;"
    out.println ""
    out.println "        ${t.className} old = ${daoObjStr}.selectById(entity.get${objectName(t.keyName,true)}());"
    out.println "        ${daoObjStr}.update(entity);"
    out.println "        saveUpdateHistory(HistoryInfo.builder().pk(entity.get${objectName(t.keyName,true)}()).before(old).after(entity).build());"
    out.println "        return RestMessage.success();"
    out.println "    }"
    out.println ""
    out.println "    /**"
    out.println "     * 根据主键字符串删除${t.comment}"
    out.println "     * @param ids 主键字符串,多个逗号隔开"
    out.println "     * @return 执行成功返回success, 否则返回fail"
    out.println "     */"
    out.println "    @Transactional(rollbackFor = Exception.class)"
    out.println "    @Log(module = \"${moduleName}\", msg = \"根据主键字符串删除${t.comment}\")"
    out.println "    public RestMessage delete(@NotBlank(message = \"请指定${t.comment}主键字符串\") String ids) {"
    out.println "        String[] ida = Helper.splitToArray(ids);"
    out.println "        for(String id : ida) {"
    out.println "            ${t.className} old = ${daoObjStr}.selectById(id);"
    out.println "            if(old == null) throw new AppException(\"${moduleName}\", \"根据主键字符串删除${t.comment}失败, 无效的主键:\" + id);"
    out.println "            ${daoObjStr}.delete(id);"
    out.println "            saveDeleteHistory(HistoryInfo.builder().pk(id).before(old).build());"
    out.println "        }"
    out.println "        return RestMessage.success();"
    out.println "    }"
    out.println ""
    out.println "    /**"
    out.println "     * 根据主键查询单个${t.comment}"
    out.println "     * @param id 主键"
    out.println "     * @return 返回单个数据对象"
    out.println "     */"
    out.println "    public ${t.className} selectById(@NotBlank(message = \"请指定${t.comment}主键\") String id) {"
    out.println "        return ${daoObjStr}.selectById(id);"
    out.println "    }"
    out.println ""
    out.println "    ///**"
    out.println "    // * 根据编码查询单个${t.comment}"
    out.println "    // * @param code 编码"
    out.println "    // * @return 返回单个数据对象"
    out.println "    // */"
    out.println "    //public ${t.className} selectByCode(@NotBlank(message = \"请指定${t.comment}编码\") String code) {"
    out.println "    //    return ${daoObjStr}.selectByCode(code);"
    out.println "    //}"
    out.println ""
    out.println "    /**"
    out.println "     * 查询${t.comment}列表"
    out.println "     * @param filter 过滤条件"
    out.println "     * @return 返回符合条件的数据集合"
    out.println "     */"
    out.println "    public List<${t.className}> selectList(${t.className}Filter filter) {"
    out.println "        return ${daoObjStr}.selectList(filter);"
    out.println "    }"
    out.println ""
    out.println "    /**"
    out.println "     * 查询${t.comment}分页列表"
    out.println "     * @param filter 过滤条件"
    out.println "     * @return 返回符合条件的分页数据集合"
    out.println "     */"
    out.println "    public Page<${t.className}> selectPageList(${t.className}Filter filter) {"
    out.println "        return ${daoObjStr}.selectPageList(filter);"
    out.println "    }"
    out.println "}"
}

def generateDao(Writer out, DbTable t) {
    out.println "package ${basePackageName}.dao;"
    out.println ""
    out.println "import com.xci.core.annotation.Paged;"
    out.println "import com.xci.core.internal.Const;"
    out.println "import com.github.pagehelper.Page;"
    out.println "import ${basePackageName}.entity.${t.className};"
    out.println "import ${basePackageName}.filter.${t.className}Filter;"
    out.println "import org.apache.ibatis.annotations.Param;"
    out.println ""
    out.println "import java.util.List;"
    out.println ""
    out.println "/**"
    out.println " * ${t.comment}数据层"
    out.println " * @author ${author}"
    out.println " */"
    out.println "public interface ${t.className}Dao {"
    out.println "//\t/**"
    out.println "//\t * 检测${t.comment}主键是否存在"
    out.println "//\t * @param id 主键"
    out.println "//\t * @return 如果存在返回true, 否则返回false"
    out.println "//\t */"
    out.println "//\tboolean existById(@Param(\"${t.keyName}\") String ${t.keyName});"
    out.println ""
    out.println "//\t/**"
    out.println "//\t * 检测${t.comment}名称是否存在"
    out.println "//\t * @param name 名称"
    out.println "//\t * @param ${t.keyName} 排除的主键"
    out.println "//\t * @return 如果存在返回true, 否则返回false"
    out.println "//\t */"
    out.println "//\tboolean existByName(@Param(\"name\") String name, @Param(\"${t.keyName}\") String ${t.keyName});"
    out.println ""
    out.println "//\t/**"
    out.println "//\t * 检测${t.comment}编码是否存在"
    out.println "//\t * @param code 编码"
    out.println "//\t * @param ${t.keyName} 排除的主键"
    out.println "//\t * @return 如果存在返回true, 否则返回false"
    out.println "//\t */"
    out.println "//\tboolean existByCode(@Param(\"code\") String code, @Param(\"${t.keyName}\") String ${t.keyName});"
    out.println ""
    out.println "\t/**"
    out.println "\t * 新建${t.comment}"
    out.println "\t * @param entity 数据对象"
    out.println "\t * @return 返回影响的行数"
    out.println "\t */"
    out.println "\tint insert(@Param(\"entity\") ${t.className} entity);"
    out.println ""
    out.println "\t/**"
    out.println "\t * 修改${t.comment}"
    out.println "\t * @param entity 数据对象"
    out.println "\t * @return 返回影响的行数"
    out.println "\t */"
    out.println "\tint update(@Param(\"entity\") ${t.className} entity);"
    out.println ""
    out.println "\t/**"
    out.println "\t * 根据主键删除${t.comment}"
    out.println "\t * @param ${t.keyName} 主键"
    out.println "\t * @return 返回影响的行数"
    out.println "\t */"
    out.println "\tint delete(@Param(\"${t.keyName}\") String ${t.keyName});"
    out.println ""
    out.println "\t/**"
    out.println "\t * 根据主键查询单个${t.comment} "
    out.println "\t * @param ${t.keyName} 主键"
    out.println "\t * @return 返回单个数据对象"
    out.println "\t */"
    out.println "\t${t.className} selectById(@Param(\"${t.keyName}\") String ${t.keyName});"
    out.println ""
    out.println "//\t/**"
    out.println "//\t * 根据编码查询单个${t.comment} "
    out.println "//\t * @param code 编码"
    out.println "//\t * @return 返回单个数据对象"
    out.println "//\t */"
    out.println "//\t${t.className} selectByCode(@Param(\"code\") String code);"
    out.println ""
    out.println "\t/**"
    out.println "\t * 查询${t.comment}列表"
    out.println "\t * @param filter 过滤条件"
    out.println "\t * @return 返回符合条件的数据集合"
    out.println "\t */"
    out.println "\tList<${t.className}> selectList(@Param(\"filter\") ${t.className}Filter filter);"
    out.println ""
    out.println "\t/**"
    out.println "\t * 查询${t.comment}分页列表"
    out.println "\t * @param filter 过滤条件"
    out.println "\t * @return  返回符合条件的分页数据集合"
    out.println "\t */"
    out.println "\t@Paged(defaultSortName = \"${t.keyName}\", defaultSortDir = Const.DESC)"
    out.println "\tPage<${t.className}> selectPageList(@Param(\"filter\") ${t.className}Filter filter);"
    out.println "}"
}

def generateMapper(Writer out, DbTable t) {
    def fullClassName = basePackageName + ".entity." + t.className
    out.println "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>"
    out.println "<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">"
    out.println "<mapper namespace=\"${basePackageName}.dao.${t.className}Dao\">"
    out.println "\t<!--${t.comment}数据层-->"
    out.println "\t<!--@author ${author}-->"
    out.println ""
    out.println "\t<!--定义公共列-->"
    out.println "\t<sql id=\"columns\">"
    out.print "\t\t"
    def columsIndex = 0
    t.fields.each() {
        out.print "${it.name}"
        if (t.fields.size() - 1 == columsIndex) out.println ""
        else out.print ","
        columsIndex++
    }
    out.println "\t</sql>"
    out.println ""
    out.println "\t<!--定义公共过滤条件-->"
    out.println "\t<sql id=\"where\">"
    out.println "\t    <where>"
    t.fields.each() {
        if (it.type == "Date") {
            out.println "\t\t<!--<if test=\"filter.${it.javaName} != null\">-->"
            out.println "\t\t   <!--<![CDATA[ and ${it.name} <= #{filter.${it.javaName}} ]]>-->"
            out.println "\t\t   <!--<![CDATA[ and ${it.name} >= #{filter.${it.javaName}} ]]>-->"
            out.println "\t\t<!--</if>-->"
        } else if (it.type == "Long" || it.type == "Integer" || it.type == "Double" || it.type == "Boolean") {
            out.println "\t\t<!--<if test=\"filter.${it.javaName} != null\">-->"
            out.println "\t\t   <!--and ${it.name} = #{filter.${it.javaName}}-->"
            out.println "\t\t<!--</if>-->"
        } else {
            out.println "\t\t<!--<if test=\"filter.${it.javaName} != null and filter.${it.javaName} != ''\">-->"
            out.println "\t\t   <!--<bind name=\"s${it.javaName}\" value=\"'%' + filter.${it.javaName} + '%'\"/>-->"
            out.println "\t\t   <!--and ${it.name} like #{s${it.javaName}}-->"
            out.println "\t\t   <!--and ${it.name} = #{filter.${it.javaName}}-->"
            out.println "\t\t<!--</if>-->"
        }
    }
    out.println "\t    </where>"
    out.println "\t</sql>"
    out.println ""
    out.println "\t<!--检测${t.comment}主键是否存在-->"
    out.println "\t<!--<select id=\"existById\" resultType=\"boolean\">-->"
    out.println "\t<!--    select count(1)-->"
    out.println "\t<!--    from ${t.name}-->"
    out.println "\t<!--    where ${t.key} = #{${t.keyName}}-->"
    out.println "\t<!--</select>-->"
    out.println ""
    out.println "\t<!--检测${t.comment}名称是否存在-->"
    out.println "\t<!--<select id=\"existByName\" resultType=\"boolean\">-->"
    out.println "\t<!--    select count(1)-->"
    out.println "\t<!--    from ${t.name}-->"
    out.println "\t<!--    where name = #{name}-->"
    out.println "\t<!--    <if test=\"${t.keyName} != null and ${t.keyName} != ''\">and ${t.key} != #{${t.keyName}}</if>-->"
    out.println "\t<!--</select>-->"
    out.println ""
    out.println "\t<!--检测${t.comment}编码是否存在-->"
    out.println "\t<!--<select id=\"existByCode\" resultType=\"boolean\">-->"
    out.println "\t<!--    select count(1)-->"
    out.println "\t<!--    from ${t.name}-->"
    out.println "\t<!--    where code = #{code}-->"
    out.println "\t<!--    <if test=\"${t.keyName} != null and ${t.keyName} != ''\">and ${t.key} != #{${t.keyName}}</if>-->"
    out.println "\t<!--</select>-->"
    out.println ""
    out.println "\t<!--新建${t.comment}-->"
    out.println "\t<insert id=\"insert\">"
    out.println "\t    insert into ${t.name} (<include refid=\"columns\"/>) values"
    out.println "\t    ("
    def insertIndex = 0
    t.fields.each() {
        out.print "\t    #{entity.${it.javaName}}"
        if (t.fields.size() - 1 != insertIndex) out.println ","
        else out.println ""
        insertIndex++
    }
    out.println "\t    )"
    out.println "\t</insert>"
    out.println ""
    out.println "\t<!--新建${t.comment}(动态新建)-->"
    out.println "\t<!--<insert id=\"insertDynamic\">-->"
    out.println "\t<!--   insert into ${t.name}-->"
    out.println "\t<!--   <trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">-->"
    t.fields.each() {
        out.println "\t<!--   \t<if test=\"entity.${it.javaName} != null\">${it.name},</if>-->"
    }
    out.println "\t<!--   </trim>-->"
    out.println "\t<!--   <trim prefix=\"values (\" suffix=\")\" suffixOverrides=\",\">-->"
    t.fields.each() {
        out.println "\t<!--   \t<if test=\"entity.${it.javaName} != null\">#{entity.${it.javaName}},</if>-->"
    }
    out.println "\t<!--   </trim>-->"
    out.println "\t<!--</insert>-->"
    out.println ""
    out.println "\t<!--修改${t.comment}-->"
    out.println "\t<update id=\"update\">"
    out.println "\t    update ${t.name}"
    out.println "\t    set "
    def updateIndex = 0
    t.fields.each() {
        if (it.name == t.key) return true
        out.print "\t        ${it.name} = #{entity.${it.javaName}}"
        if (t.fields.size() - 2 != updateIndex) out.println ","
        else out.println ""
        updateIndex++
    }
    out.println "\t    where ${t.key} = #{entity.${t.keyName}}"
    out.println "\t</update>"
    out.println ""
    out.println "\t<!--修改${t.comment}(动态修改)-->"
    out.println "\t<!--<update id=\"updateDynamic\">-->"
    out.println "\t<!--    update ${t.name}-->"
    out.println "\t<!--    <set>-->"
    t.fields.each() {
        if (it.name != t.key) {
            out.println "\t<!--        <if test=\"entity.${it.javaName} != null\">${it.name} = #{entity.${it.javaName}},</if>-->"
            updateIndex++
        }
    }
    out.println "\t<!--    </set>-->"
    out.println "\t<!--    where ${t.key} = #{entity.${t.keyName}}-->"
    out.println "\t<!--</update>-->"
    out.println ""
    out.println "\t<!--根据主键删除${t.comment}-->"
    out.println "\t<delete id=\"delete\">"
    out.println "\t    delete"
    out.println "\t    from ${t.name}"
    out.println "\t    where ${t.key} = #{${t.keyName}}"
    out.println "\t</delete>"
    out.println ""
    out.println "\t<!--根据主键查询单个${t.comment}-->"
    out.println "\t<select id=\"selectById\" resultType=\"${fullClassName}\">"
    out.println "\t    select"
    out.println "\t    <include refid=\"columns\"/>"
    out.println "\t    from ${t.name}"
    out.println "\t    where ${t.key} = #{${t.keyName}}"
    out.println "\t</select>"
    out.println ""
    out.println "\t<!--根据编码查询单个${t.comment}-->"
    out.println "\t<!--<select id=\"selectByCode\" resultType=\"${fullClassName}\">-->"
    out.println "\t<!--    select-->"
    out.println "\t<!--    <include refid=\"columns\"/>-->"
    out.println "\t<!--    from ${t.name}-->"
    out.println "\t<!--    where code = #{code}-->"
    out.println "\t<!--</select>-->"
    out.println ""
    out.println "\t<!--查询${t.comment}列表-->"
    out.println "\t<select id=\"selectList\" resultType=\"${fullClassName}\">"
    out.println "\t    select"
    out.println "\t    <include refid=\"columns\"/>"
    out.println "\t    from ${t.name}"
    out.println "\t    <include refid=\"where\"/>"
    out.println "\t    order by ${t.key} desc"
    out.println "\t</select>"
    out.println ""
    out.println "\t<!--查询${t.comment}分页列表-->"
    out.println "\t<select id=\"selectPageList\" resultType=\"${fullClassName}\">"
    out.println "\t    select"
    out.println "\t    <include refid=\"columns\"/>"
    out.println "\t    from ${t.name}"
    out.println "\t    <include refid=\"where\"/>"
    out.println "\t</select>"
    out.println "</mapper>"
}

//#endregion

//#region 生成客户端代码

def generateClientEntity(Writer out, DbTable t) {
    out.println "//==================================================================="
    out.println "// 西交投.NET产品框架 版权所有"
    out.println "//==================================================================="
    out.println "using System;"
    out.println "using System.ComponentModel.DataAnnotations;"
    out.println "using XCI.Data.Entity;"
    out.println ""
    out.println "namespace ${clientNamespace}.Entity"
    out.println "{"
    out.println "    /// <summary>"
    out.println "    /// ${t.comment}"
    out.println "    /// @author ${author}"
    out.println "    /// </summary>"
    out.println "    [Table(\"${t.comment}\")]"
    out.println "    public class ${t.className}"
    out.println "    {"
    t.fields.each() {
        def colComment = clearColumnOptions(it.comment)
        out.println "        /// <summary>"
        out.println "        /// ${it.comment}"
        out.println "        /// </summary>"

        if (it.isKey) {
            out.println "        [Primary]"
        }
        //#region 验证
        if (it.dasColumn.isNotNull()) {
            out.println "        [Required(ErrorMessage = \"请输入${colComment}\")]"
        }
        if (it.type == "String") {
            def size = it.size > 0 ? it.size : 500
            out.println "        [StringLength(${size}, ErrorMessage = \"${colComment}长度不能超过${size}\")]";
        }
        //#endregion
        if (isIdColumn(it) || it.csharpName.equalsIgnoreCase("Remark")) {
            out.println "        [Column(\"${colComment}\", false)]"
        } else {
            out.println "        [Column(\"${colComment}\")]"
        }
        out.println "        public ${getClientModelFieldType(it)} ${it.csharpName} { get; set; }"
        out.println ""
    }
    out.println "        /// <summary>"
    out.println "        /// 复制对象"
    out.println "        /// </summary>"
    out.println "        public ${t.className} Clone()"
    out.println "        {"
    out.println "            return (${t.className})MemberwiseClone();"
    out.println "        }"
    out.println "    }"
    out.println "}"
}

def generateClientFilter(Writer out, DbTable t) {
    out.println "//==================================================================="
    out.println "// 西交投.NET产品框架 版权所有"
    out.println "//==================================================================="
    out.println "using System;"
    out.println "using System.Collections.Generic;"
    out.println "using System.ComponentModel.DataAnnotations;"
    out.println ""
    out.println "namespace ${clientNamespace}.Filter"
    out.println "{"
    out.println "   /// <summary>"
    out.println "   /// ${t.comment}过滤条件"
    out.println "   /// @author ${author}"
    out.println "   /// </summary>"
    out.println "   public class ${t.className}Filter"
    out.println "   {"
    t.fields.each() {
        out.println "       /// <summary>"
        out.println "       /// ${it.comment}"
        out.println "       /// </summary>"
        out.println "       [Display(Name = \"${clearColumnOptions(it.comment)}\")]"
        out.println "       public ${getClientFilterFieldType(it)} ${it.csharpName} { get; set; }"
        out.println ""
    }
    out.println "       /*"
    out.println "       /// <summary>"
    out.println "       /// 处理结束日期"
    out.println "       /// </summary>"
    out.println "       public void ProcessEndDateTime()"
    out.println "       {"
    out.println "           if (OperateEndDateTime != null)"
    out.println "           {"
    out.println "               OperateEndDateTime = OperateEndDateTime.Value.AddDays(1);"
    out.println "           }"
    out.println "       }"
    out.println "       */"
    out.println "   }"
    out.println "}"
}

def generateClientService(Writer out, DbTable t) {
    def tFieldName = objectName(t.name, false);
    out.println "//==================================================================="
    out.println "// 西交投.NET产品框架 版权所有"
    out.println "//==================================================================="
    out.println "using System;"
    out.println "using System.Collections.Generic;"
    out.println "using ${clientNamespace}.Entity;"
    out.println "using ${clientNamespace}.Filter;"
    out.println "using XCI.Core;"
    out.println "using XCI.Sys.Service;"
    out.println ""
    out.println "namespace ${clientNamespace}.Service"
    out.println "{"
    out.println "    /// <summary>"
    out.println "    /// ${t.comment}服务"
    out.println "    /// @author ${author}"
    out.println "    /// </summary>"
    out.println "    public class ${t.className}Service : BaseService<${t.className}>"
    out.println "    {"
    out.println "        public static readonly ${t.className}Service Instance = new ${t.className}Service();//默认实例"
    out.println "        protected override string RootUrl => \"/api/${moduleName}/${tFieldName}/\";//接口根路径前缀"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 初始化${t.comment}对象"
    out.println "        /// </summary>"
    out.println "        public ${t.className} New()"
    out.println "        {"
    out.println "            return new ${t.className}"
    out.println "            {"
    out.println "                ${objectName(t.key, true)} = NewId()"
    out.println "            };"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 复制${t.comment}对象"
    out.println "        /// </summary>"
    out.println "        /// <param name=\"original\">原对象</param>"
    out.println "        public ${t.className} Copy(${t.className} original)"
    out.println "        {"
    out.println "            if (original == null) throw new ArgumentNullException(nameof(original));"
    out.println "            var entity = original?.Clone();"
    out.println "            entity.${objectName(t.key, true)} = NewId();"
    out.println "            return entity;"
    out.println "        }"
    out.println ""
    out.println "        /*"
    out.println "        /// <summary>"
    out.println "        /// 检查${t.comment}名称是否存在"
    out.println "        /// </summary>"
    out.println "        /// <param name=\"name\">名称</param>"
    out.println "        /// <param name=\"${t.keyName}\">排除的主键</param>"
    out.println "        public bool ExistByName(string name,string ${t.keyName} = null)"
    out.println "        {"
    out.println "            return ExistByNameCore(name,${t.keyName});"
    out.println "        }"
    out.println "        /// <summary>"
    out.println "        /// 检查${t.comment}编码是否存在"
    out.println "        /// </summary>"
    out.println "        /// <param name=\"code\">编码</param>"
    out.println "        /// <param name=\"${t.keyName}\">排除的主键</param>"
    out.println "        public bool ExistByCode(string code,string ${t.keyName} = null)"
    out.println "        {"
    out.println "            return ExistByCodeCore(code,${t.keyName});"
    out.println "        }"
    out.println "        */"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 新建${t.comment}"
    out.println "        /// </summary>"
    out.println "        /// <param name=\"entity\">数据对象</param>"
    out.println "        /// <returns>成功返回true,否则返回false</returns>"
    out.println "        public bool Insert(${t.className} entity)"
    out.println "        {"
    out.println "            return InsertCore(entity);"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 修改${t.comment}"
    out.println "        /// </summary>"
    out.println "        /// <param name=\"entity\">数据对象</param>"
    out.println "        /// <returns>成功返回true,否则返回false</returns>"
    out.println "        public bool Update(${t.className} entity)"
    out.println "        {"
    out.println "            return UpdateCore(entity);"
    out.println "        }"
    out.println ""
    out.println "        /*"
    out.println "        /// <summary>"
    out.println "        /// 根据主键删除${t.comment}"
    out.println "        /// </summary>"
    out.println "        /// <param name=\"${t.keyName}\">主键</param>"
    out.println "        /// <returns>成功返回true,否则返回false</returns>"
    out.println "        public bool DeleteById(string ${t.keyName})"
    out.println "        {"
    out.println "            return DeleteByIdCore(${t.keyName});"
    out.println "        }"
    out.println "        */"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 根据主键字符串删除${t.comment}"
    out.println "        /// </summary>"
    out.println "        /// <param name=\"ids\">主键字符串,多个主键之间逗号隔开</param>"
    out.println "        /// <returns>成功返回true,否则返回false</returns>"
    out.println "        public bool Delete(string ids)"
    out.println "        {"
    out.println "            return DeleteByIdsCore(ids);"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 根据主键获取单个${t.comment}对象"
    out.println "        /// </summary>"
    out.println "        /// <param name=\"${t.keyName}\">主键</param>"
    out.println "        /// <returns>成功返回单个数据对象</returns>"
    out.println "        public ${t.className} SelectById(string ${t.keyName})"
    out.println "        {"
    out.println "            return SelectByIdCore(${t.keyName});"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 根据编码获取单个${t.comment}对象"
    out.println "        /// </summary>"
    out.println "        /// <param name=\"code\">编码</param>"
    out.println "        /// <returns>成功返回单个数据对象</returns>"
    out.println "        public ${t.className} SelectByCode(string code)"
    out.println "        {"
    out.println "            return SelectByCodeCore(code);"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 查询${t.comment}列表"
    out.println "        /// </summary>"
    out.println "        /// <param name=\"filter\">过滤条件</param>"
    out.println "        public List<${t.className}> SelectList(${t.className}Filter filter)"
    out.println "        {"
    out.println "            var map = Map.New.AddObject(filter);"
    out.println "            return SelectListCore(map);"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 查询${t.comment}分页列表"
    out.println "        /// </summary>"
    out.println "        /// <param name=\"paged\">分页对象</param>"
    out.println "        /// <param name=\"filter\">过滤条件</param>"
    out.println "        public PageList<${t.className}> SelectPageList(PageParam paged, ${t.className}Filter filter)"
    out.println "        {"
    out.println "            var map = ToPageMap(paged).AddObject(filter);"
    out.println "            return SelectPageListCore(map);"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 导出${t.comment}列表"
    out.println "        /// </summary>"
    out.println "        /// <param name=\"filter\">过滤条件</param>"
    out.println "        public byte[] Export(${t.className}Filter filter)"
    out.println "        {"
    out.println "            var map = Map.New.AddObject(filter);"
    out.println "            return ExportCore(map);"
    out.println "        }"
    out.println "    }"
    out.println "}"
}

def generateClientManagerForm(Writer out, DbTable t) {
    out.println "//==================================================================="
    out.println "// 西交投.NET产品框架 版权所有"
    out.println "//==================================================================="
    def tFieldName = objectName(t.name, false)
    out.println "using System;"
    out.println "using ${clientNamespace}.Entity;"
    out.println "using ${clientNamespace}.Filter;"
    out.println "using ${clientNamespace}.Service;"
    out.println "using XCI.Extensions;"
    out.println "using XCI.Sys;"
    out.println "using XCI.Sys.Forms;"
    out.println "using XCI.Windows;"
    out.println "using XCI.Windows.Extensions;"
    out.println "using XCI.Windows.Helper;"
    out.println ""
    out.println "namespace ${clientNamespace}.Forms"
    out.println "{"
    out.println "    /// <summary>"
    out.println "    /// ${t.comment}管理"
    out.println "    /// @author ${author}"
    out.println "    /// </summary>"
    out.println "    [FormCaption(\"${t.comment}管理\")]"
    out.println "    public partial class Frm${t.className}Manager : FrmSysBase"
    out.println "    {"
    out.println "        /// <summary>"
    out.println "        /// 表格操作类"
    out.println "        /// </summary>"
    out.println "        private GridHelper<${t.className}> helper;"
    out.println "        private readonly bool allowSelect = SysRuntime.IsAuthorize(\"${moduleName}.${tFieldName}.select\");"
    out.println "        private readonly bool allowInsert = SysRuntime.IsAuthorize(\"${moduleName}.${tFieldName}.insert\");"
    out.println "        private readonly bool allowUpdate = SysRuntime.IsAuthorize(\"${moduleName}.${tFieldName}.update\");"
    out.println "        private readonly bool allowDelete = SysRuntime.IsAuthorize(\"${moduleName}.${tFieldName}.delete\");"
    out.println "        private readonly bool allowExport = SysRuntime.IsAuthorize(\"${moduleName}.${tFieldName}.export\");"
    out.println ""
    out.println "        public Frm${t.className}Manager()"
    out.println "        {"
    out.println "            InitializeComponent();"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 页面初始化"
    out.println "        /// </summary>"
    out.println "        private void InitForm()"
    out.println "        {"
    out.println "            #region 初始化GridHelper"
    out.println ""
    out.println "            helper = new GridHelper<${t.className}>(gridView);"
    out.println "            helper.ElapsedTimeItem = labExecuteTime;"
    out.println ""
    out.println "            #endregion"
    out.println ""
    out.println "            #region 初始化控件"
    out.println "            //BeginInit(() => { filterStatus.SelectedIndex = 1; });"
    out.println "            #endregion"
    out.println ""
    out.println "            #region 注册表格事件"
    out.println "            //排序事件"
    out.println "            gridView.StartSorting += (o, e) => { helper.OnServerStartSorting(); };"
    out.println "            gridView.ColumnSortInfoChanged += (o, e) => { LoadData(); };"
    out.println ""
    out.println "            //上下方向键控制光标移动事件"
    out.println "            gridView.KeyDown += (o, e) => { helper.OnUpDownMoveFocusRow(e); };"
    out.println ""
    out.println "            //选中项变化事件"
    out.println "            gridView.SelectionChanged += (o, e) => { ButtonStatus(); };"
    out.println "            //分页事件"
    out.println "            gridPager.PageChanged += (o, e) => { LoadData(); };"
    out.println ""
    out.println "            //双击事件"
    out.println "            gridView.DoubleClick += (o, e) => { helper.DblClickRow(e, () => btnProperty.PerformClick()); };"
    out.println "            #endregion"
    out.println ""
    out.println "            LoadFirstPageData();"
    out.println "            ButtonStatus();"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 加载第一页数据"
    out.println "        /// </summary>"
    out.println "        private void LoadFirstPageData() => LoadData(1);"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 数据加载"
    out.println "        /// </summary>"
    out.println "        /// <param name=\"pageIndex\">页码</param>"
    out.println "        private void LoadData(int? pageIndex = null)"
    out.println "        {"
    out.println "            if (!allowSelect) return;"
    out.println ""
    out.println "            helper.SetPageIndex(pageIndex);"
    out.println "            var paged = helper.Paged;"
    out.println "            var filter = GetFilter();"
    out.println ""
    out.println "            helper.RunCallback(e =>"
    out.println "            {"
    out.println "                e.Result = ${t.className}Service.Instance.SelectPageList(paged, filter);"
    out.println "            })"
    out.println "            .CompletCallback(e =>"
    out.println "            {"
    out.println "                ButtonStatus();"
    out.println "            })"
    out.println "            .RunAsync();"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 获取过滤条件"
    out.println "        /// </summary>"
    out.println "        public ${t.className}Filter GetFilter()"
    out.println "        {"
    out.println "            var filter = new ${t.className}Filter();"
    out.println "            WinHelper.GetFormData(panelFilter, filter);"
    out.println "            return filter;"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 数据编辑"
    out.println "        /// </summary>"
    out.println "        /// <param name=\"created\">是否新建</param>"
    out.println "        /// <param name=\"model\">模型对象</param>"
    out.println "        private void Edit(bool created, ${t.className} model)"
    out.println "        {"
    out.println "            var editForm = new Frm${t.className}Edit();"
    out.println "            editForm.SetModel(created, model);"
    out.println "            editForm.ShowDialogOk(LoadFirstPageData);"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 设置按钮状态"
    out.println "        /// </summary>"
    out.println "        private void ButtonStatus()"
    out.println "        {"
    out.println "            btnNew.Enabled = allowInsert;"
    out.println "            btnEdit.Enabled = helper.HasSingleSelected && allowUpdate;"
    out.println "            btnCopy.Enabled = helper.HasSingleSelected && allowInsert;"
    out.println "            btnDelete.Enabled = helper.HasSelected && allowDelete;"
    out.println "            btnExport.Enabled = helper.HasData && allowExport;"
    out.println "            btnProperty.Enabled = helper.HasSingleSelected;"
    out.println "            btnSearch.Enabled = allowSelect;"
    out.println ""
    out.println "            /*"
    out.println "            btnNew.Visibility = GetBtnVisibility(allowInsert);"
    out.println "            btnEdit.Visibility = GetBtnVisibility(allowUpdate);"
    out.println "            btnDelete.Visibility = GetBtnVisibility(allowDelete);"
    out.println "            btnCopy.Visibility = GetBtnVisibility(allowInsert);"
    out.println "            btnExport.Visibility = GetBtnVisibility(allowExport);"
    out.println "            btnSearch.Visible = allowSelect;"
    out.println "            */"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 页面加载事件"
    out.println "        /// </summary>"
    out.println "        private void Frm${t.className}Manager_Load(object sender, EventArgs e)"
    out.println "        {"
    out.println "            InitForm();"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 关键字文本框键盘事件"
    out.println "        /// </summary>"
    out.println "        private void FilterName_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)"
    out.println "        {"
    out.println "            //回车事件回调"
    out.println "            EnterPressHandle(e, LoadFirstPageData);"
    out.println "        }"
    out.println ""
    out.println "        private void FilterStatus_SelectedValueChanged(object sender, EventArgs e)"
    out.println "        {"
    out.println "            EndInit(LoadFirstPageData);"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 搜索按钮事件"
    out.println "        /// </summary>"
    out.println "        private void BtnSearch_Click(object sender, EventArgs e)"
    out.println "        {"
    out.println "            //加载首页数据"
    out.println "            LoadFirstPageData();"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 重置查询条件"
    out.println "        /// </summary>"
    out.println "        private void BtnReset_Click(object sender, EventArgs e)"
    out.println "        {"
    out.println "            BeginInit(() =>"
    out.println "            {"
    out.println "                WinHelper.SetFormData(panelFilter, new ${t.className}Filter());"
    out.println "            });"
    out.println "            LoadFirstPageData();"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 新建事件"
    out.println "        /// </summary>"
    out.println "        private void BtnNew_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)"
    out.println "        {"
    out.println "            Edit(true, ${t.className}Service.Instance.New());"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 复制事件"
    out.println "        /// </summary>"
    out.println "        private void BtnCopy_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)"
    out.println "        {"
    out.println "            Edit(true, ${t.className}Service.Instance.Copy(helper.Selected));"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 修改事件"
    out.println "        /// </summary>"
    out.println "        private void BtnEdit_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)"
    out.println "        {"
    out.println "            Edit(false, helper.Selected);"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 删除事件"
    out.println "        /// </summary>"
    out.println "        private void BtnDelete_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)"
    out.println "        {"
    out.println "            helper.Delete(() =>"
    out.println "            {"
    out.println "                ${t.className}Service.Instance.Delete(helper.SelectedIds).IfTrue(() =>"
    out.println "                {"
    out.println "                    LoadData();"
    out.println "                });"
    out.println "            });"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 导出事件"
    out.println "        /// </summary>"
    out.println "        private void BtnExport_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)"
    out.println "        {"
    out.println "            var filter = GetFilter();"
    out.println "            ExcelExportCore(() => ${t.className}Service.Instance.Export(filter), \"${t.comment}\");"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 属性事件"
    out.println "        /// </summary>"
    out.println "        private void BtnProperty_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)"
    out.println "        {"
    out.println "            var detailsForm = new Frm${t.className}Details(helper);"
    out.println "            detailsForm.ShowDialogOk();"
    out.println "        }"
    out.println "    }"
    out.println "}"
}

def generateClientManagerDesignerForm(Writer out, DbTable t) {
    out.println "//==================================================================="
    out.println "// 西交投.NET产品框架 版权所有"
    out.println "//==================================================================="
    out.println "namespace ${clientNamespace}.Forms"
    out.println "{"
    out.println "    partial class Frm${t.className}Manager"
    out.println "    {"
    out.println "        /// <summary>"
    out.println "        /// Required designer variable."
    out.println "        /// </summary>"
    out.println "        private System.ComponentModel.IContainer components = null;"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// Clean up any resources being used."
    out.println "        /// </summary>"
    out.println "        /// <param name=\"disposing\">true if managed resources should be disposed; otherwise, false.</param>"
    out.println "        protected override void Dispose(bool disposing)"
    out.println "        {"
    out.println "            if (disposing && (components != null))"
    out.println "            {"
    out.println "                components.Dispose();"
    out.println "            }"
    out.println "            base.Dispose(disposing);"
    out.println "        }"
    out.println ""
    out.println "        #region Windows Form Designer generated code"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// Required method for Designer support - do not modify"
    out.println "        /// the contents of this method with the code editor."
    out.println "        /// </summary>"
    out.println "        private void InitializeComponent()"
    out.println "        {"
    out.println "            this.components = new System.ComponentModel.Container();"
    out.println "            this.barManager1 = new DevExpress.XtraBars.BarManager(this.components);"
    out.println "            this.bar1 = new DevExpress.XtraBars.Bar();"
    out.println "            this.barController1 = new DevExpress.XtraBars.BarAndDockingController(this.components);"
    out.println "            this.barDockControlTop = new DevExpress.XtraBars.BarDockControl();"
    out.println "            this.barDockControlBottom = new DevExpress.XtraBars.BarDockControl();"
    out.println "            this.barDockControlLeft = new DevExpress.XtraBars.BarDockControl();"
    out.println "            this.barDockControlRight = new DevExpress.XtraBars.BarDockControl();"
    out.println "            this.btnNew = new DevExpress.XtraBars.BarButtonItem();"
    out.println "            this.btnCopy = new DevExpress.XtraBars.BarButtonItem();"
    out.println "            this.btnEdit = new DevExpress.XtraBars.BarButtonItem();"
    out.println "            this.btnDelete = new DevExpress.XtraBars.BarButtonItem();"
    out.println "            this.btnProperty = new DevExpress.XtraBars.BarButtonItem();"
    out.println "            this.btnExport = new DevExpress.XtraBars.BarButtonItem();"
    out.println "            this.labExecuteTime = new DevExpress.XtraBars.BarStaticItem();"
    out.println "            this.panelFilter = new System.Windows.Forms.Panel();"
    out.println "            this.labFilterName = new DevExpress.XtraEditors.LabelControl();"
    out.println "            this.filterName = new DevExpress.XtraEditors.TextEdit();"
    out.println "            this.labFilterStatus = new DevExpress.XtraEditors.LabelControl();"
    out.println "            this.filterStatus = new XCI.Sys.Controls.StatusComboBoxEdit();"
    out.println "            this.btnSearch = new DevExpress.XtraEditors.SimpleButton();"
    out.println "            this.btnReset = new DevExpress.XtraEditors.SimpleButton();"
    out.println "            this.gridControl = new XCI.Windows.Controls.GridControlx();"
    out.println "            this.gridView = new XCI.Windows.Controls.GridViewx();"
    out.println "            this.gridPager = new XCI.Windows.Controls.GridPager();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.barManager1)).BeginInit();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.barController1)).BeginInit();"
    out.println "            this.panelFilter.SuspendLayout();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.filterName.Properties)).BeginInit();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.filterStatus.Properties)).BeginInit();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.gridControl)).BeginInit();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.gridView)).BeginInit();"
    out.println "            this.SuspendLayout();"
    out.println "            //"
    out.println "            // barManager1"
    out.println "            //"
    out.println "            this.barManager1.AllowCustomization = false;"
    out.println "            this.barManager1.AllowItemAnimatedHighlighting = false;"
    out.println "            this.barManager1.AllowMoveBarOnToolbar = false;"
    out.println "            this.barManager1.AllowQuickCustomization = false;"
    out.println "            this.barManager1.AllowShowToolbarsPopup = false;"
    out.println "            this.barManager1.Bars.AddRange(new DevExpress.XtraBars.Bar[] {this.bar1});"
    out.println "            this.barManager1.Controller = this.barController1;"
    out.println "            this.barManager1.DockControls.Add(this.barDockControlTop);"
    out.println "            this.barManager1.DockControls.Add(this.barDockControlBottom);"
    out.println "            this.barManager1.DockControls.Add(this.barDockControlLeft);"
    out.println "            this.barManager1.DockControls.Add(this.barDockControlRight);"
    out.println "            this.barManager1.Form = this;"
    out.println "            this.barManager1.HideBarsWhenMerging = false;"
    out.println "            this.barManager1.Items.AddRange(new DevExpress.XtraBars.BarItem[] {"
    out.println "            this.btnNew,"
    out.println "            this.btnEdit,"
    out.println "            this.btnCopy,"
    out.println "            this.btnDelete,"
    out.println "            this.btnExport,"
    out.println "            this.btnProperty,"
    out.println "            this.labExecuteTime});"
    out.println "            this.barManager1.MainMenu = this.bar1;"
    out.println "            this.barManager1.MaxItemId = 8;"
    out.println "            //"
    out.println "            // bar1"
    out.println "            //"
    out.println "            this.bar1.BarName = \"Tools\";"
    out.println "            this.bar1.CanDockStyle = DevExpress.XtraBars.BarCanDockStyle.Top;"
    out.println "            this.bar1.DockCol = 0;"
    out.println "            this.bar1.DockRow = 0;"
    out.println "            this.bar1.DockStyle = DevExpress.XtraBars.BarDockStyle.Top;"
    out.println "            this.bar1.FloatLocation = new System.Drawing.Point(300, 225);"
    out.println "            this.bar1.LinksPersistInfo.AddRange(new DevExpress.XtraBars.LinkPersistInfo[] {"
    out.println "            new DevExpress.XtraBars.LinkPersistInfo(this.labExecuteTime),"
    out.println "            new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.btnNew, DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph),"
    out.println "            new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.btnCopy, DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph),"
    out.println "            new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.btnEdit, DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph),"
    out.println "            new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.btnDelete, DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph),"
    out.println "            new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.btnExport, DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph),"
    out.println "            new DevExpress.XtraBars.LinkPersistInfo(DevExpress.XtraBars.BarLinkUserDefines.PaintStyle, this.btnProperty, DevExpress.XtraBars.BarItemPaintStyle.CaptionGlyph)});"
    out.println "            this.bar1.OptionsBar.AllowQuickCustomization = false;"
    out.println "            this.bar1.OptionsBar.DisableClose = true;"
    out.println "            this.bar1.OptionsBar.DisableCustomization = true;"
    out.println "            this.bar1.OptionsBar.DrawDragBorder = false;"
    out.println "            this.bar1.OptionsBar.UseWholeRow = true;"
    out.println "            this.bar1.Text = \"工具栏\";"
    out.println "            //"
    out.println "            // barController1"
    out.println "            //"
    out.println "            this.barController1.PropertiesBar.AllowLinkLighting = false;"
    out.println "            //"
    out.println "            // barDockControlTop"
    out.println "            //"
    out.println "            this.barDockControlTop.CausesValidation = false;"
    out.println "            this.barDockControlTop.Dock = System.Windows.Forms.DockStyle.Top;"
    out.println "            this.barDockControlTop.Location = new System.Drawing.Point(0, 0);"
    out.println "            this.barDockControlTop.Manager = this.barManager1;"
    out.println "            this.barDockControlTop.Size = new System.Drawing.Size(662, 24);"
    out.println "            //"
    out.println "            // barDockControlBottom"
    out.println "            //"
    out.println "            this.barDockControlBottom.CausesValidation = false;"
    out.println "            this.barDockControlBottom.Dock = System.Windows.Forms.DockStyle.Bottom;"
    out.println "            this.barDockControlBottom.Location = new System.Drawing.Point(0, 448);"
    out.println "            this.barDockControlBottom.Manager = this.barManager1;"
    out.println "            this.barDockControlBottom.Size = new System.Drawing.Size(662, 0);"
    out.println "            //"
    out.println "            // barDockControlLeft"
    out.println "            //"
    out.println "            this.barDockControlLeft.CausesValidation = false;"
    out.println "            this.barDockControlLeft.Dock = System.Windows.Forms.DockStyle.Left;"
    out.println "            this.barDockControlLeft.Location = new System.Drawing.Point(0, 24);"
    out.println "            this.barDockControlLeft.Manager = this.barManager1;"
    out.println "            this.barDockControlLeft.Size = new System.Drawing.Size(0, 424);"
    out.println "            //"
    out.println "            // barDockControlRight"
    out.println "            //"
    out.println "            this.barDockControlRight.CausesValidation = false;"
    out.println "            this.barDockControlRight.Dock = System.Windows.Forms.DockStyle.Right;"
    out.println "            this.barDockControlRight.Location = new System.Drawing.Point(662, 24);"
    out.println "            this.barDockControlRight.Manager = this.barManager1;"
    out.println "            this.barDockControlRight.Size = new System.Drawing.Size(0, 424);"
    out.println "            //"
    out.println "            // btnNew"
    out.println "            //"
    out.println "            this.btnNew.Caption = \"新建\";"
    out.println "            this.btnNew.Enabled = false;"
    out.println "            this.btnNew.Id = 1;"
    out.println "            this.btnNew.Name = \"btnNew\";"
    out.println "            this.btnNew.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.BtnNew_ItemClick);"
    out.println "            //"
    out.println "            // btnCopy"
    out.println "            //"
    out.println "            this.btnCopy.Caption = \"复制\";"
    out.println "            this.btnCopy.Enabled = false;"
    out.println "            this.btnCopy.Id = 2;"
    out.println "            this.btnCopy.Name = \"btnCopy\";"
    out.println "            this.btnCopy.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.BtnCopy_ItemClick);"
    out.println "            //"
    out.println "            // btnEdit"
    out.println "            //"
    out.println "            this.btnEdit.Caption = \"修改\";"
    out.println "            this.btnEdit.Enabled = false;"
    out.println "            this.btnEdit.Id = 3;"
    out.println "            this.btnEdit.Name = \"btnEdit\";"
    out.println "            this.btnEdit.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.BtnEdit_ItemClick);"
    out.println "            //"
    out.println "            // btnDelete"
    out.println "            //"
    out.println "            this.btnDelete.Caption = \"删除\";"
    out.println "            this.btnDelete.Enabled = false;"
    out.println "            this.btnDelete.Id = 4;"
    out.println "            this.btnDelete.Name = \"btnDelete\";"
    out.println "            this.btnDelete.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.BtnDelete_ItemClick);"
    out.println "            //"
    out.println "            // btnExport"
    out.println "            //"
    out.println "            this.btnExport.Caption = \"导出\";"
    out.println "            this.btnExport.Enabled = false;"
    out.println "            this.btnExport.Id = 5;"
    out.println "            this.btnExport.Name = \"btnExport\";"
    out.println "            this.btnExport.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.BtnExport_ItemClick);"
    out.println "            // "
    out.println "            // btnProperty"
    out.println "            // "
    out.println "            this.btnProperty.Caption = \"属性\";"
    out.println "            this.btnProperty.Enabled = false;"
    out.println "            this.btnProperty.Id = 6;"
    out.println "            this.btnProperty.Name = \"btnProperty\";"
    out.println "            this.btnProperty.ItemClick += new DevExpress.XtraBars.ItemClickEventHandler(this.BtnProperty_ItemClick);"
    out.println "            //"
    out.println "            // labExecuteTime"
    out.println "            //"
    out.println "            this.labExecuteTime.Alignment = DevExpress.XtraBars.BarItemLinkAlignment.Right;"
    out.println "            this.labExecuteTime.Border = DevExpress.XtraEditors.Controls.BorderStyles.NoBorder;"
    out.println "            this.labExecuteTime.Caption = \"耗时\";"
    out.println "            this.labExecuteTime.Id = 7;"
    out.println "            this.labExecuteTime.Name = \"labExecuteTime\";"
    out.println "            //"
    out.println "            // panelFilter"
    out.println "            //"
    out.println "            this.panelFilter.Controls.Add(this.labFilterName);"
    out.println "            this.panelFilter.Controls.Add(this.filterName);"
    out.println "            this.panelFilter.Controls.Add(this.labFilterStatus);"
    out.println "            this.panelFilter.Controls.Add(this.filterStatus);"
    out.println "            this.panelFilter.Controls.Add(this.btnSearch);"
    out.println "            this.panelFilter.Controls.Add(this.btnReset);"
    out.println "            this.panelFilter.Dock = System.Windows.Forms.DockStyle.Top;"
    out.println "            this.panelFilter.Location = new System.Drawing.Point(0, 24);"
    out.println "            this.panelFilter.Margin = new System.Windows.Forms.Padding(0);"
    out.println "            this.panelFilter.Name = \"panelFilter\";"
    out.println "            this.panelFilter.Size = new System.Drawing.Size(662, 42);"
    out.println "            this.panelFilter.TabIndex = 0;"
    out.println "            //"
    out.println "            // labFilterName"
    out.println "            //"
    out.println "            this.labFilterName.Location = new System.Drawing.Point(12, 12);"
    out.println "            this.labFilterName.Name = \"labFilterName\";"
    out.println "            this.labFilterName.Size = new System.Drawing.Size(52, 19);"
    out.println "            this.labFilterName.TabIndex = 0;"
    out.println "            this.labFilterName.Text = \"关键字：\";"
    out.println "            //"
    out.println "            // filterName"
    out.println "            //"
    out.println "            this.filterName.Location = new System.Drawing.Point(70, 9);"
    out.println "            this.filterName.MenuManager = this.barManager1;"
    out.println "            this.filterName.Name = \"filterName\";"
    out.println "            this.filterName.Size = new System.Drawing.Size(200, 26);"
    out.println "            this.filterName.TabIndex = 1;"
    out.println "            this.filterName.Tag = \"Name\";"
    out.println "            this.filterName.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.FilterName_KeyPress);"
    out.println "            // "
    out.println "            // labFilterStatus"
    out.println "            // "
    out.println "            this.labFilterStatus.Location = new System.Drawing.Point(276, 12);"
    out.println "            this.labFilterStatus.Name = \"labFilterStatus\";"
    out.println "            this.labFilterStatus.Size = new System.Drawing.Size(39, 19);"
    out.println "            this.labFilterStatus.TabIndex = 6;"
    out.println "            this.labFilterStatus.Text = \"状态：\";"
    out.println "            // "
    out.println "            // filterStatus"
    out.println "            // "
    out.println "            this.filterStatus.Location = new System.Drawing.Point(321, 9);"
    out.println "            this.filterStatus.MenuManager = this.barManager1;"
    out.println "            this.filterStatus.Name = \"filterStatus\";"
    out.println "            this.filterStatus.Properties.Buttons.AddRange(new DevExpress.XtraEditors.Controls.EditorButton[] {"
    out.println "                    new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Combo)});"
    out.println "            this.filterStatus.Size = new System.Drawing.Size(83, 26);"
    out.println "            this.filterStatus.TabIndex = 3;"
    out.println "            this.filterStatus.Tag = \"Status\";"
    out.println "            this.filterStatus.SelectedValueChanged += new System.EventHandler(this.FilterStatus_SelectedValueChanged);"
    out.println "            //"
    out.println "            // btnSearch"
    out.println "            //"
    out.println "            this.btnSearch.Location = new System.Drawing.Point(410, 6);"
    out.println "            this.btnSearch.Name = \"btnSearch\";"
    out.println "            this.btnSearch.Size = new System.Drawing.Size(80, 30);"
    out.println "            this.btnSearch.TabIndex = 2;"
    out.println "            this.btnSearch.Text = \"查询\";"
    out.println "            this.btnSearch.Click += new System.EventHandler(this.BtnSearch_Click);"
    out.println "            // "
    out.println "            // btnReset"
    out.println "            // "
    out.println "            this.btnReset.Location = new System.Drawing.Point(496, 6);"
    out.println "            this.btnReset.Name = \"btnReset\";"
    out.println "            this.btnReset.Size = new System.Drawing.Size(80, 30);"
    out.println "            this.btnReset.TabIndex = 3;"
    out.println "            this.btnReset.Text = \"重置\";"
    out.println "            this.btnReset.Click += new System.EventHandler(this.BtnReset_Click);"
    out.println "            //"
    out.println "            // gridControl"
    out.println "            //"
    out.println "            this.gridControl.Dock = System.Windows.Forms.DockStyle.Fill;"
    out.println "            this.gridControl.Id = \"${UUID.randomUUID().toString().replace("-", "")}\";"
    out.println "            this.gridControl.Location = new System.Drawing.Point(0, 66);"
    out.println "            this.gridControl.MainView = this.gridView;"
    out.println "            this.gridControl.MenuManager = this.barManager1;"
    out.println "            this.gridControl.Name = \"gridControl\";"
    out.println "            this.gridControl.Pager = this.gridPager;"
    out.println "            this.gridControl.TabIndex = 1;"
    out.println "            this.gridControl.ViewCollection.AddRange(new DevExpress.XtraGrid.Views.Base.BaseView[] {this.gridView});"
    out.println "            //"
    out.println "            // gridView"
    out.println "            //"
    out.println "            this.gridView.GridControl = this.gridControl;"
    out.println "            this.gridView.IndicatorWidth = 35;"
    out.println "            this.gridView.Name = \"gridView\";"
    out.println "            this.gridView.OptionsBehavior.Editable = false;"
    out.println "            this.gridView.OptionsView.ShowGroupPanel = false;"
    out.println "            //"
    out.println "            // gridPager"
    out.println "            //"
    out.println "            this.gridPager.Dock = System.Windows.Forms.DockStyle.Bottom;"
    out.println "            this.gridPager.Location = new System.Drawing.Point(0, 419);"
    out.println "            this.gridPager.Margin = new System.Windows.Forms.Padding(3, 2, 3, 2);"
    out.println "            this.gridPager.Name = \"gridPager\";"
    out.println "            this.gridPager.Size = new System.Drawing.Size(662, 30);"
    out.println "            this.gridPager.TabIndex = 2;"
    out.println "            //"
    out.println "            // Frm${t.className}Manager"
    out.println "            //"
    out.println "            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 19F);"
    out.println "            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;"
    out.println "            this.ClientSize = new System.Drawing.Size(662, 448);"
    out.println "            this.Controls.Add(this.gridControl);"
    out.println "            this.Controls.Add(this.gridPager);"
    out.println "            this.Controls.Add(this.panelFilter);"
    out.println "            this.Controls.Add(this.barDockControlLeft);"
    out.println "            this.Controls.Add(this.barDockControlRight);"
    out.println "            this.Controls.Add(this.barDockControlBottom);"
    out.println "            this.Controls.Add(this.barDockControlTop);"
    out.println "            this.Margin = new System.Windows.Forms.Padding(3, 4, 3, 4);"
    out.println "            this.MinimizeBox = false;"
    out.println "            this.Name = \"Frm${t.className}Manager\";"
    out.println "            this.ShowIcon = false;"
    out.println "            this.Text = \"${t.comment}管理\";"
    out.println "            this.Load += new System.EventHandler(this.Frm${t.className}Manager_Load);"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.barManager1)).EndInit();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.barController1)).EndInit();"
    out.println "            this.panelFilter.ResumeLayout(false);"
    out.println "            this.panelFilter.PerformLayout();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.filterName.Properties)).EndInit();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.filterStatus.Properties)).EndInit();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.gridControl)).EndInit();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.gridView)).EndInit();"
    out.println "            this.ResumeLayout(false);"
    out.println "            this.PerformLayout();"
    out.println "        }"
    out.println ""
    out.println "        #endregion"
    out.println "        private DevExpress.XtraBars.BarManager barManager1;"
    out.println "        private DevExpress.XtraBars.BarAndDockingController barController1;"
    out.println "        private DevExpress.XtraBars.BarDockControl barDockControlTop;"
    out.println "        private DevExpress.XtraBars.BarDockControl barDockControlBottom;"
    out.println "        private DevExpress.XtraBars.BarDockControl barDockControlLeft;"
    out.println "        private DevExpress.XtraBars.BarDockControl barDockControlRight;"
    out.println "        private DevExpress.XtraBars.Bar bar1;"
    out.println "        private DevExpress.XtraBars.BarButtonItem btnNew;"
    out.println "        private DevExpress.XtraBars.BarButtonItem btnCopy;"
    out.println "        private DevExpress.XtraBars.BarButtonItem btnEdit;"
    out.println "        private DevExpress.XtraBars.BarButtonItem btnDelete;"
    out.println "        private DevExpress.XtraBars.BarButtonItem btnProperty;"
    out.println "        private DevExpress.XtraBars.BarButtonItem btnExport;"
    out.println "        private DevExpress.XtraBars.BarStaticItem labExecuteTime;"
    out.println "        private System.Windows.Forms.Panel panelFilter;"
    out.println "        private DevExpress.XtraEditors.LabelControl labFilterName;"
    out.println "        private DevExpress.XtraEditors.TextEdit filterName;"
    out.println "        private DevExpress.XtraEditors.LabelControl labFilterStatus;"
    out.println "        private XCI.Sys.Controls.StatusComboBoxEdit filterStatus;"
    out.println "        private DevExpress.XtraEditors.SimpleButton btnSearch;"
    out.println "        private DevExpress.XtraEditors.SimpleButton btnReset;"
    out.println "        private XCI.Windows.Controls.GridControlx gridControl;"
    out.println "        private XCI.Windows.Controls.GridViewx gridView;"
    out.println "        private XCI.Windows.Controls.GridPager gridPager;"
    out.println "    }"
    out.println "}"
}

def generateClientEditForm(Writer out, DbTable t) {
    out.println "//==================================================================="
    out.println "// 西交投.NET产品框架 版权所有"
    out.println "//==================================================================="
    out.println "using System;"
    out.println "using System.Windows.Forms;"
    out.println "using XCI.Helper;"
    out.println "using ${clientNamespace}.Entity;"
    out.println "using ${clientNamespace}.Service;"
    out.println "using XCI.Sys.Forms;"
    out.println "using XCI.Windows;"
    out.println "using XCI.Windows.Helper;"
    out.println ""
    out.println "namespace ${clientNamespace}.Forms"
    out.println "{"
    out.println "    /// <summary>"
    out.println "    /// ${t.comment}编辑"
    out.println "    /// @author ${author}"
    out.println "    /// </summary>"
    out.println "    [FormCaption(\"${t.comment}编辑\")]"
    out.println "    public partial class Frm${t.className}Edit : FrmSysBase"
    out.println "    {"
    out.println "        private bool created;"
    out.println "        private ${t.className} model;"
    out.println ""
    out.println "        public Frm${t.className}Edit()"
    out.println "        {"
    out.println "            InitializeComponent();"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 注册Esc关闭窗体事件"
    out.println "        /// </summary>"
    out.println "        protected override bool ProcessCmdKey(ref Message msg, Keys keys)"
    out.println "        {"
    out.println "            return OnEsc(msg, keys, () => { EditFormEscResultCancel(); });"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 页面初始化"
    out.println "        /// </summary>"
    out.println "        protected void InitForm()"
    out.println "        {"
    out.println "            BindModel();"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 设置数据对象"
    out.println "        /// </summary>"
    out.println "        /// <param name=\"p_created\">是否新建</param>"
    out.println "        /// <param name=\"p_model\">数据对象</param>"
    out.println "        public void SetModel(bool p_created, ${t.className} p_model)"
    out.println "        {"
    out.println "            created = p_created;"
    out.println "            model = p_model;"
    out.println "            Text = created ? \"新建${t.comment}\" : \"修改${t.comment}\";"
    out.println "            ckContinue.Visible = ckReserve.Visible = p_created;"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 绑定数据到界面"
    out.println "        /// </summary>"
    out.println "        private void BindModel()"
    out.println "        {"
    out.println "            WinHelper.SetFormData(layoutPanel, model);"
    out.println "            //editName.Select();"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 保存数据对象"
    out.println "        /// </summary>"
    out.println "        private bool SaveModel()"
    out.println "        {"
    out.println "            WinHelper.GetFormData(layoutPanel, model);"
    out.println ""
    out.println "            var isValid = ValidModel(model);"
    out.println "            if (!isValid) return false;"
    out.println ""
    out.println "            //model.Spell = SpellHelper.GetSpell(model.Name);"
    out.println "            //model.FullSpell = SpellHelper.ConvertFullSpell(model.Name);"
    out.println "            if (created)"
    out.println "            {"
    out.println "                return ${t.className}Service.Instance.Insert(model);"
    out.println "            }"
    out.println ""
    out.println "            return ${t.className}Service.Instance.Update(model);"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 页面加载事件"
    out.println "        /// </summary>"
    out.println "        private void Frm${t.className}Edit_Load(object sender, EventArgs e)"
    out.println "        {"
    out.println "            InitForm();"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 连续保存事件"
    out.println "        /// </summary>"
    out.println "        private void CkContinue_CheckedChanged(object sender, EventArgs e)"
    out.println "        {"
    out.println "            ckReserve.Enabled = ckContinue.Checked;"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 保存事件"
    out.println "        /// </summary>"
    out.println "        private void btnSave_Click(object sender, EventArgs e)"
    out.println "        {"
    out.println "            if (!SaveModel()) return;"
    out.println "            if (ckContinue.Checked) //连续保存"
    out.println "            {"
    out.println "                MessageBoxHelper.ShowMessage(\"保存成功\");"
    out.println "                var nmodel = ckReserve.Checked ? "
    out.println "                                    ${t.className}Service.Instance.Copy(model) : "
    out.println "                                    ${t.className}Service.Instance.New();"
    out.println "                SetModel(true, nmodel);"
    out.println "                BindModel();"
    out.println "            }"
    out.println "            else"
    out.println "            {"
    out.println "                ResultOk();"
    out.println "            }"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 关闭事件"
    out.println "        /// </summary>"
    out.println "        private void btnClose_Click(object sender, EventArgs e)"
    out.println "        {"
    out.println "            //如果点关闭按钮后想刷新表格就用ResultOk,不刷新用ResultCancel"
    out.println "            ResultOk();"
    out.println "            //ResultCancel();"
    out.println "        }"
    out.println "    }"
    out.println "}"

}

def generateClientEditDesignerForm(Writer out, DbTable t) {
    out.println "//==================================================================="
    out.println "// 西交投.NET产品框架 版权所有"
    out.println "//==================================================================="
    out.println "namespace ${clientNamespace}.Forms"
    out.println "{"
    out.println "    partial class Frm${t.className}Edit"
    out.println "    {"
    out.println "        /// <summary>"
    out.println "        /// Required designer variable."
    out.println "        /// </summary>"
    out.println "        private System.ComponentModel.IContainer components = null;"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// Clean up any resources being used."
    out.println "        /// </summary>"
    out.println "        /// <param name=\"disposing\">true if managed resources should be disposed; otherwise, false.</param>"
    out.println "        protected override void Dispose(bool disposing)"
    out.println "        {"
    out.println "            if (disposing && (components != null))"
    out.println "            {"
    out.println "                components.Dispose();"
    out.println "            }"
    out.println "            base.Dispose(disposing);"
    out.println "        }"
    out.println ""
    out.println "        #region Windows Form Designer generated code"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// Required method for Designer support - do not modify"
    out.println "        /// the contents of this method with the code editor."
    out.println "        /// </summary>"
    out.println "        private void InitializeComponent()"
    out.println "        {"
    out.println "            this.layoutPanel = new DevExpress.XtraLayout.LayoutControl();"
    out.println "            this.layoutControlGroup1 = new DevExpress.XtraLayout.LayoutControlGroup();"
    t.fields.each() {
        if (it.name == t.key) return true
        out.println "            this.edit${it.csharpName} = new DevExpress.XtraEditors.TextEdit();"
        out.println "            this.layoutItem${it.csharpName} = new DevExpress.XtraLayout.LayoutControlItem();"
    }
    out.println "            this.panelFooter = new System.Windows.Forms.Panel();"
    out.println "            this.labLine = new DevExpress.XtraEditors.LabelControl();"
    out.println "            this.ckReserve = new DevExpress.XtraEditors.CheckEdit();"
    out.println "            this.ckContinue = new DevExpress.XtraEditors.CheckEdit();"
    out.println "            this.btnSave = new DevExpress.XtraEditors.SimpleButton();"
    out.println "            this.btnClose = new DevExpress.XtraEditors.SimpleButton();"
    out.println "            this.emptySpaceItem1 = new DevExpress.XtraLayout.EmptySpaceItem();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.layoutPanel)).BeginInit();"
    out.println "            this.layoutPanel.SuspendLayout();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.layoutControlGroup1)).BeginInit();"
    t.fields.each() {
        if (it.name == t.key) return true
        out.println "            ((System.ComponentModel.ISupportInitialize)(this.edit${it.csharpName}.Properties)).BeginInit();"
        out.println "            ((System.ComponentModel.ISupportInitialize)(this.layoutItem${it.csharpName})).BeginInit();"
    }
    out.println "            this.panelFooter.SuspendLayout();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.ckReserve.Properties)).BeginInit();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.ckContinue.Properties)).BeginInit();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.emptySpaceItem1)).BeginInit();"
    out.println "            this.SuspendLayout();"
    out.println "            //"
    out.println "            // layoutPanel"
    out.println "            //"
    out.println "            this.layoutPanel.AllowCustomization = false;"
    t.fields.each() {
        if (it.name == t.key) return true
        out.println "            this.layoutPanel.Controls.Add(this.edit${it.csharpName});"
    }
    out.println "            this.layoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;"
    out.println "            this.layoutPanel.Location = new System.Drawing.Point(0, 0);"
    out.println "            this.layoutPanel.Name = \"layoutPanel\";"
    out.println "            this.layoutPanel.OptionsCustomizationForm.DesignTimeCustomizationFormPositionAndSize = new System.Drawing.Rectangle(790, 225, 650, 400);"
    out.println "            this.layoutPanel.Root = this.layoutControlGroup1;"
    out.println "            this.layoutPanel.Size = new System.Drawing.Size(556, 486);"
    out.println "            this.layoutPanel.TabIndex = 0;"
    out.println "            this.layoutPanel.Text = \"layoutPanel\";"
    out.println "            //"
    out.println "            // layoutControlGroup1"
    out.println "            //"
    out.println "            this.layoutControlGroup1.EnableIndentsWithoutBorders = DevExpress.Utils.DefaultBoolean.True;"
    out.println "            this.layoutControlGroup1.GroupBordersVisible = false;"
    out.println "            this.layoutControlGroup1.Items.AddRange(new DevExpress.XtraLayout.BaseLayoutItem[] {"
    t.fields.each() {
        if (it.name == t.key) return true
        out.println "            this.layoutItem${it.csharpName},"
    }
    out.println "            this.emptySpaceItem1});"
    out.println "            this.layoutControlGroup1.Name = \"Root\";"
    out.println "            this.layoutControlGroup1.Size = new System.Drawing.Size(556, 486);"
    out.println "            this.layoutControlGroup1.TextVisible = false;"
    def index = 0
    t.fields.each() {
        if (it.name == t.key) return true
        out.println "            //"
        out.println "            // edit${it.csharpName}"
        out.println "            //"
        out.println "            this.edit${it.csharpName}.EnterMoveNextControl = true;"
        out.println "            this.edit${it.csharpName}.Location = new System.Drawing.Point(86, ${index * 30 + 12});"
        out.println "            this.edit${it.csharpName}.Name = \"edit${it.csharpName}\";"
        if (it.type == "String") {
            def size = it.size > 0 ? it.size : 500
            out.println "            this.edit${it.csharpName}.Properties.MaxLength = ${size};"
        }
        out.println "            this.edit${it.csharpName}.Size = new System.Drawing.Size(402, 26);"
        out.println "            this.edit${it.csharpName}.StyleController = this.layoutPanel;"
        out.println "            this.edit${it.csharpName}.TabIndex = ${index};"
        out.println "            this.edit${it.csharpName}.Tag = \"${it.csharpName}\";"
        out.println ""
        out.println "            //"
        out.println "            // layoutItem${it.csharpName}"
        out.println "            //"
        if (it.dasColumn.isNotNull()) {
            out.println "            this.layoutItem${it.csharpName}.AllowHtmlStringInCaption = true;"
            out.println "            this.layoutItem${it.csharpName}.Text = \"${clearColumnOptions(it.comment)}<color=red>*</color>：\";"
        } else {
            out.println "            this.layoutItem${it.csharpName}.Text = \"${clearColumnOptions(it.comment)}：\";"
        }
        out.println "            this.layoutItem${it.csharpName}.Control = this.edit${it.csharpName};"
        out.println "            this.layoutItem${it.csharpName}.Location = new System.Drawing.Point(0, ${index * 30});"
        out.println "            this.layoutItem${it.csharpName}.Size = new System.Drawing.Size(519, 30);"
        out.println "            this.layoutItem${it.csharpName}.TextSize = new System.Drawing.Size(110, 19);"
        out.println "            this.layoutItem${it.csharpName}.Name = \"layoutItem${it.csharpName}\";"
        index++
    }
    out.println "            // "
    out.println "            // emptySpaceItem1"
    out.println "            // "
    out.println "            this.emptySpaceItem1.AllowHotTrack = false;"
    out.println "            this.emptySpaceItem1.Location = new System.Drawing.Point(0, 900);"
    out.println "            this.emptySpaceItem1.Name = \"emptySpaceItem1\";"
    out.println "            this.emptySpaceItem1.Size = new System.Drawing.Size(519, 10);"
    out.println "            this.emptySpaceItem1.TextSize = new System.Drawing.Size(0, 0);"
    out.println "            // "
    out.println "            // panelFooter"
    out.println "            // "
    out.println "            this.panelFooter.Controls.Add(this.labLine);"
    out.println "            this.panelFooter.Controls.Add(this.ckReserve);"
    out.println "            this.panelFooter.Controls.Add(this.ckContinue);"
    out.println "            this.panelFooter.Controls.Add(this.btnSave);"
    out.println "            this.panelFooter.Controls.Add(this.btnClose);"
    out.println "            this.panelFooter.Dock = System.Windows.Forms.DockStyle.Bottom;"
    out.println "            this.panelFooter.Location = new System.Drawing.Point(0, 486);"
    out.println "            this.panelFooter.Name = \"panelFooter\";"
    out.println "            this.panelFooter.Size = new System.Drawing.Size(556, 53);"
    out.println "            this.panelFooter.TabIndex = 1;"
    out.println "            // "
    out.println "            // labLine"
    out.println "            // "
    out.println "            this.labLine.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) "
    out.println "            | System.Windows.Forms.AnchorStyles.Right)));"
    out.println "            this.labLine.AutoSizeMode = DevExpress.XtraEditors.LabelAutoSizeMode.None;"
    out.println "            this.labLine.LineLocation = DevExpress.XtraEditors.LineLocation.Center;"
    out.println "            this.labLine.LineOrientation = DevExpress.XtraEditors.LabelLineOrientation.Horizontal;"
    out.println "            this.labLine.LineVisible = true;"
    out.println "            this.labLine.Location = new System.Drawing.Point(0, -5);"
    out.println "            this.labLine.Name = \"labLine\";"
    out.println "            this.labLine.Size = new System.Drawing.Size(556, 10);"
    out.println "            this.labLine.TabIndex = 4;"
    out.println "            // "
    out.println "            // ckContinue"
    out.println "            // "
    out.println "            this.ckContinue.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));"
    out.println "            this.ckContinue.Location = new System.Drawing.Point(12, 15);"
    out.println "            this.ckContinue.Name = \"ckContinue\";"
    out.println "            this.ckContinue.Properties.AllowFocused = false;"
    out.println "            this.ckContinue.Properties.Caption = \"连续保存\";"
    out.println "            this.ckContinue.Size = new System.Drawing.Size(75, 23);"
    out.println "            this.ckContinue.TabIndex = 1;"
    out.println "            this.ckContinue.Visible = false;"
    out.println "            this.ckContinue.CheckedChanged += new System.EventHandler(this.CkContinue_CheckedChanged);"
    out.println "            // "
    out.println "            // ckReserve"
    out.println "            // "
    out.println "            this.ckReserve.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));"
    out.println "            this.ckReserve.Enabled = false;"
    out.println "            this.ckReserve.Location = new System.Drawing.Point(93, 15);"
    out.println "            this.ckReserve.Name = \"ckReserve\";"
    out.println "            this.ckReserve.Properties.AllowFocused = false;"
    out.println "            this.ckReserve.Properties.Caption = \"不清空\";"
    out.println "            this.ckReserve.Size = new System.Drawing.Size(75, 23);"
    out.println "            this.ckReserve.TabIndex = 2;"
    out.println "            this.ckReserve.Visible = false;"
    out.println "            // "
    out.println "            // btnSave"
    out.println "            // "
    out.println "            this.btnSave.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));"
    out.println "            this.btnSave.Location = new System.Drawing.Point(378, 11);"
    out.println "            this.btnSave.Name = \"btnSave\";"
    out.println "            this.btnSave.Size = new System.Drawing.Size(80, 30);"
    out.println "            this.btnSave.TabIndex = 0;"
    out.println "            this.btnSave.Text = \"保存(&S)\";"
    out.println "            this.btnSave.Click += new System.EventHandler(this.btnSave_Click);"
    out.println "            // "
    out.println "            // btnClose"
    out.println "            // "
    out.println "            this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));"
    out.println "            this.btnClose.DialogResult = System.Windows.Forms.DialogResult.Cancel;"
    out.println "            this.btnClose.Location = new System.Drawing.Point(464, 11);"
    out.println "            this.btnClose.Name = \"btnClose\";"
    out.println "            this.btnClose.Size = new System.Drawing.Size(80, 30);"
    out.println "            this.btnClose.TabIndex = 1;"
    out.println "            this.btnClose.Text = \"关闭(&C)\";"
    out.println "            this.btnClose.Click += new System.EventHandler(this.btnClose_Click);"
    out.println "            // "
    out.println "            // Frm${t.className}Edit"
    out.println "            // "
    out.println "            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 19F);"
    out.println "            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;"
    out.println "            this.ClientSize = new System.Drawing.Size(556, 539);"
    out.println "            this.Controls.Add(this.layoutPanel);"
    out.println "            this.Controls.Add(this.panelFooter);"
    out.println "            this.MinimizeBox = false;"
    out.println "            this.MinimumSize = new System.Drawing.Size(370, 300);"
    out.println "            this.Name = \"Frm${t.className}Edit\";"
    out.println "            this.ShowIcon = false;"
    out.println "            this.ShowInTaskbar = false;"
    out.println "            this.Text = \"${t.comment}编辑\";"
    out.println "            this.Load += new System.EventHandler(this.Frm${t.className}Edit_Load);"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.layoutPanel)).EndInit();"
    out.println "            this.layoutPanel.ResumeLayout(false);"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.layoutControlGroup1)).EndInit();"
    t.fields.each() {
        if (it.name == t.key) return true
        out.println "            ((System.ComponentModel.ISupportInitialize)(this.edit${it.csharpName}.Properties)).EndInit();"
        out.println "            ((System.ComponentModel.ISupportInitialize)(this.layoutItem${it.csharpName})).EndInit();"
    }
    out.println "            this.panelFooter.ResumeLayout(false);"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.ckReserve.Properties)).EndInit();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.ckContinue.Properties)).EndInit();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.emptySpaceItem1)).EndInit();"
    out.println "            this.ResumeLayout(false);"
    out.println "        }"
    out.println ""
    out.println "        #endregion"

    out.println "        private DevExpress.XtraLayout.LayoutControl layoutPanel;"
    out.println "        private DevExpress.XtraLayout.LayoutControlGroup layoutControlGroup1;"
    t.fields.each() {
        if (it.name == t.key) return true
        out.println "        private DevExpress.XtraEditors.TextEdit edit${it.csharpName};"
        out.println "        private DevExpress.XtraLayout.LayoutControlItem layoutItem${it.csharpName};"
    }
    out.println "        private DevExpress.XtraLayout.EmptySpaceItem emptySpaceItem1;"
    out.println "        private System.Windows.Forms.Panel panelFooter;"
    out.println "        private DevExpress.XtraEditors.LabelControl labLine;"
    out.println "        private DevExpress.XtraEditors.CheckEdit ckContinue;"
    out.println "        private DevExpress.XtraEditors.CheckEdit ckReserve;"
    out.println "        private DevExpress.XtraEditors.SimpleButton btnSave;"
    out.println "        private DevExpress.XtraEditors.SimpleButton btnClose;"
    out.println "    }"
    out.println "}"
}

def generateClientDetailsForm(Writer out, DbTable t) {
    out.println "//==================================================================="
    out.println "// 西交投.NET产品框架 版权所有"
    out.println "//==================================================================="
    out.println "using System;"
    out.println "using System.Windows.Forms;"
    out.println "using ${clientNamespace}.Entity;"
    out.println "using ${clientNamespace}.Service;"
    out.println "using XCI.Helper;"
    out.println "using XCI.Sys.Forms;"
    out.println "using XCI.Sys.Service;"
    out.println "using XCI.Windows;"
    out.println "using XCI.Windows.Helper;"
    out.println ""
    out.println "namespace ${clientNamespace}.Forms"
    out.println "{"
    out.println "    /// <summary>"
    out.println "    /// ${t.comment}详情"
    out.println "    /// @author ${author}"
    out.println "    /// </summary>"
    out.println "    [FormCaption(\"${t.comment}详情\")]"
    out.println "    public partial class Frm${t.className}Details : FrmSysBase"
    out.println "    {"
    out.println "        private TabDelayLoad delayLoad;"
    out.println "        private readonly GridHelper<${t.className}> helper;"
    out.println "        private string id;"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 使用主键初始化"
    out.println "        /// </summary>"
    out.println "        public Frm${t.className}Details(string id)"
    out.println "        {"
    out.println "            InitializeComponent();"
    out.println "            this.id = id;"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 使用GridHelper初始化"
    out.println "        /// </summary>"
    out.println "        public Frm${t.className}Details(GridHelper<${t.className}> helper)"
    out.println "        {"
    out.println "            InitializeComponent();"
    out.println "            this.helper = helper;"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 注册Esc关闭窗体事件"
    out.println "        /// </summary>"
    out.println "        protected override bool ProcessCmdKey(ref Message msg, Keys keys)"
    out.println "        {"
    out.println "            return OnEsc(msg, keys, DetailsFormEscResultCancel);"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 页面初始化"
    out.println "        /// </summary>"
    out.println "        protected void InitForm()"
    out.println "        {"
    out.println "            if (helper == null)//如果GridHelper为空，则隐藏上一条和下一条按钮"
    out.println "            {"
    out.println "                btnPrev.Visible = false;"
    out.println "                btnNext.Visible = false;"
    out.println "            }"
    out.println "            BindModel();"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 绑定数据到界面"
    out.println "        /// </summary>"
    out.println "        private void BindModel()"
    out.println "        {"
    out.println "            //设置按钮状态"
    out.println "            SetButtonStatus();"
    out.println "            //初始化延迟加载对象"
    out.println "            delayLoad = new TabDelayLoad(tabPane1, TabPanel_Load);"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 选项卡加载处理"
    out.println "        /// </summary>"
    out.println "        /// <param name=\"name\">Tab页名称</param>"
    out.println "        protected void TabPanel_Load(string name)"
    out.println "        {"
    out.println "            switch (name)"
    out.println "            {"
    out.println "                case nameof(tabGeneral)://查询基本信息"
    out.println "                    var model = ${t.className}Service.Instance.SelectById(id);"
    out.println "                    if (model != null)"
    out.println "                    {"
    out.println "                        WinHelper.SetFormData(layoutPanel, model);//把数据绑定到界面上"
    out.println "                    }"
    out.println "                    #region 读取历史记录表中最后一次的操作记录"
    out.println "                    //var hmodel = HistoryService.Instance.SelectLastByPrimaryKey(id);"
    out.println "                    //if (hmodel != null)"
    out.println "                    //{"
    out.println "                    //    editOperateUserName.Text = hmodel.OperateUserName;"
    out.println "                    //    editOperateDateTime.Text = DateTimeHelper.FormatDateHasSecond(hmodel.OperateDateTime);"
    out.println "                    //}"
    out.println "                    #endregion"
    out.println "                    break;"
    out.println "                case nameof(tabHistory):"
    out.println "                    //历史记录"
    out.println "                    historyGridControl.PrimaryKey = id;"
    out.println "                    break;"
    out.println "            }"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 设置按钮状态"
    out.println "        /// </summary>"
    out.println "        private void SetButtonStatus()"
    out.println "        {"
    out.println "            if (helper != null) //如果传入的是GridHelper对象，可以启用上一条和下一条"
    out.println "            {"
    out.println "                id = helper.SelectedId;"
    out.println "                btnPrev.Enabled = !helper.IsFirstFocus;"
    out.println "                btnNext.Enabled = !helper.IsLastFocus;"
    out.println "            }"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 页面加载事件"
    out.println "        /// </summary>"
    out.println "        private void Frm${t.className}Details_Load(object sender, EventArgs e)"
    out.println "        {"
    out.println "            InitForm();"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 上一条事件"
    out.println "        /// </summary>"
    out.println "        private void BtnPrev_Click(object sender, EventArgs e)"
    out.println "        {"
    out.println "            helper.View.MovePrev();"
    out.println "            BindModel();"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 下一条事件"
    out.println "        /// </summary>"
    out.println "        private void BtnNext_Click(object sender, EventArgs e)"
    out.println "        {"
    out.println "            helper.View.MoveNext();"
    out.println "            BindModel();"
    out.println "        }"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// 关闭窗口事件"
    out.println "        /// </summary>"
    out.println "        private void BtnClose_Click(object sender, EventArgs e)"
    out.println "        {"
    out.println "            ResultCancel();"
    out.println "        }"
    out.println "    }"
    out.println "}"
}

def generateClientDetailsDesignerForm(Writer out, DbTable t) {
    out.println "//==================================================================="
    out.println "// 西交投.NET产品框架 版权所有"
    out.println "//==================================================================="
    out.println "namespace ${clientNamespace}.Forms"
    out.println "{"
    out.println "    partial class Frm${t.className}Details"
    out.println "    {"
    out.println "        /// <summary>"
    out.println "        /// Required designer variable."
    out.println "        /// </summary>"
    out.println "        private System.ComponentModel.IContainer components = null;"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// Clean up any resources being used."
    out.println "        /// </summary>"
    out.println "        /// <param name=\"disposing\">true if managed resources should be disposed; otherwise, false.</param>"
    out.println "        protected override void Dispose(bool disposing)"
    out.println "        {"
    out.println "            if (disposing && (components != null))"
    out.println "            {"
    out.println "                components.Dispose();"
    out.println "            }"
    out.println "            base.Dispose(disposing);"
    out.println "        }"
    out.println ""
    out.println "        #region Windows Form Designer generated code"
    out.println ""
    out.println "        /// <summary>"
    out.println "        /// Required method for Designer support - do not modify"
    out.println "        /// the contents of this method with the code editor."
    out.println "        /// </summary>"
    out.println "        private void InitializeComponent()"
    out.println "        {"
    out.println "            this.tabPane1 = new DevExpress.XtraBars.Navigation.TabPane();"
    out.println "            this.tabGeneral = new DevExpress.XtraBars.Navigation.TabNavigationPage();"
    out.println "            this.tabHistory = new DevExpress.XtraBars.Navigation.TabNavigationPage();"
    out.println "            this.layoutPanel = new DevExpress.XtraLayout.LayoutControl();"
    out.println "            this.layoutControlGroup1 = new DevExpress.XtraLayout.LayoutControlGroup();"
    t.fields.each() {
        out.println "            this.edit${it.csharpName} = new DevExpress.XtraEditors.TextEdit();"
        out.println "            this.layoutItem${it.csharpName} = new DevExpress.XtraLayout.LayoutControlItem();"
    }
    out.println "            this.historyGridControl = new XCI.Sys.Controls.RecordHistoryGridUserControl();"
    out.println "            this.panelFooter = new System.Windows.Forms.Panel();"
    out.println "            this.lableLine = new DevExpress.XtraEditors.LabelControl();"
    out.println "            this.btnNext = new DevExpress.XtraEditors.SimpleButton();"
    out.println "            this.btnPrev = new DevExpress.XtraEditors.SimpleButton();"
    out.println "            this.btnClose = new DevExpress.XtraEditors.SimpleButton();"
    out.println "            this.panelFooter.SuspendLayout();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.tabPane1)).BeginInit();"
    out.println "            this.tabPane1.SuspendLayout();"
    out.println "            this.tabGeneral.SuspendLayout();"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.layoutPanel)).BeginInit();"
    out.println "            this.layoutPanel.SuspendLayout();"
    t.fields.each() {
        out.println "            ((System.ComponentModel.ISupportInitialize)(this.edit${it.csharpName}.Properties)).BeginInit();"
        out.println "            ((System.ComponentModel.ISupportInitialize)(this.layoutItem${it.csharpName})).BeginInit();"
    }
    out.println "            this.tabHistory.SuspendLayout();"
    out.println "            this.SuspendLayout();"
    out.println "            //"
    out.println "            // tabPane1"
    out.println "            //"
    out.println "            this.tabPane1.Controls.Add(this.tabGeneral);"
    out.println "            this.tabPane1.Controls.Add(this.tabHistory);"
    out.println "            this.tabPane1.Dock = System.Windows.Forms.DockStyle.Fill;"
    out.println "            this.tabPane1.Location = new System.Drawing.Point(0, 0);"
    out.println "            this.tabPane1.Name = \"tabPane1\";"
    out.println "            this.tabPane1.Pages.AddRange(new DevExpress.XtraBars.Navigation.NavigationPageBase[] {"
    out.println "            this.tabGeneral,"
    out.println "            this.tabHistory});"
    out.println "            this.tabPane1.RegularSize = new System.Drawing.Size(461, 332);"
    out.println "            this.tabPane1.SelectedPage = this.tabGeneral;"
    out.println "            this.tabPane1.Size = new System.Drawing.Size(461, 332);"
    out.println "            this.tabPane1.TabIndex = 0;"
    out.println "            this.tabPane1.Text = \"tabPane1\";"
    out.println "            //"
    out.println "            // tabGeneral"
    out.println "            //"
    out.println "            this.tabGeneral.Caption = \"基本信息\";"
    out.println "            this.tabGeneral.Controls.Add(this.layoutPanel);"
    out.println "            this.tabGeneral.Name = \"tabGeneral\";"
    out.println "            this.tabGeneral.Size = new System.Drawing.Size(461, 299);"
    out.println "            //"
    out.println "            // tabHistory"
    out.println "            //"
    out.println "            this.tabHistory.Caption = \"历史记录\";"
    out.println "            this.tabHistory.Controls.Add(this.historyGridControl);"
    out.println "            this.tabHistory.Name = \"tabHistory\";"
    out.println "            this.tabHistory.Size = new System.Drawing.Size(461, 299);"
    out.println "            //"
    out.println "            // layoutPanel"
    out.println "            //"
    out.println "            this.layoutPanel.AllowCustomization = false;"
    t.fields.each() {
        out.println "            this.layoutPanel.Controls.Add(this.edit${it.csharpName});"
    }
    out.println "            this.layoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;"
    out.println "            this.layoutPanel.Location = new System.Drawing.Point(0, 0);"
    out.println "            this.layoutPanel.Name = \"layoutPanel\";"
    out.println "            this.layoutPanel.Root = this.layoutControlGroup1;"
    out.println "            this.layoutPanel.Size = new System.Drawing.Size(461, 299);"
    out.println "            this.layoutPanel.TabIndex = 0;"
    out.println "            this.layoutPanel.Text = \"layoutControl1\";"
    out.println "            //"
    out.println "            // layoutControlGroup1"
    out.println "            //"
    out.println "            this.layoutControlGroup1.EnableIndentsWithoutBorders = DevExpress.Utils.DefaultBoolean.True;"
    out.println "            this.layoutControlGroup1.GroupBordersVisible = false;"
    out.println "            this.layoutControlGroup1.Items.AddRange(new DevExpress.XtraLayout.BaseLayoutItem[] {"
    def layIndex = 0
    t.fields.each() {
        out.print "            this.layoutItem${it.csharpName}"
        if (t.fields.size() - 1 != layIndex) out.println ","
        layIndex++
    }
    out.println "            });"
    out.println "            this.layoutControlGroup1.Name = \"Root\";"
    out.println "            this.layoutControlGroup1.Size = new System.Drawing.Size(461, 299);"
    out.println "            this.layoutControlGroup1.TextVisible = false;"
    def index = 0
    t.fields.each() {
        out.println "            //"
        out.println "            // edit${it.csharpName}"
        out.println "            //"
        out.println "            this.edit${it.csharpName}.EnterMoveNextControl = true;"
        out.println "            this.edit${it.csharpName}.Location = new System.Drawing.Point(80, ${index * 30 + 12});"
        out.println "            this.edit${it.csharpName}.Name = \"edit${it.csharpName}\";"
        out.println "            this.edit${it.csharpName}.Size = new System.Drawing.Size(369, 26);"
        out.println "            this.edit${it.csharpName}.StyleController = this.layoutPanel;"
        out.println "            this.edit${it.csharpName}.TabIndex = ${index};"
        out.println "            this.edit${it.csharpName}.Properties.ReadOnly = true;"
        out.println "            this.edit${it.csharpName}.Tag = \"${it.csharpName}\";"
        out.println ""
        out.println "            //"
        out.println "            // layoutItem${it.csharpName}"
        out.println "            //"
        out.println "            this.layoutItem${it.csharpName}.Text = \"${clearColumnOptions(it.comment)}：\";"
        out.println "            this.layoutItem${it.csharpName}.Control = this.edit${it.csharpName};"
        out.println "            this.layoutItem${it.csharpName}.Location = new System.Drawing.Point(0, ${index * 30});"
        out.println "            this.layoutItem${it.csharpName}.Size = new System.Drawing.Size(441, 30);"
        out.println "            this.layoutItem${it.csharpName}.TextSize = new System.Drawing.Size(65, 19);"
        out.println "            this.layoutItem${it.csharpName}.Name = \"layoutItem${it.csharpName}\";"
        index++
    }
    out.println "            //"
    out.println "            // historyGridControl"
    out.println "            //"
    out.println "            this.historyGridControl.Dock = System.Windows.Forms.DockStyle.Fill;"
    out.println "            this.historyGridControl.Location = new System.Drawing.Point(0, 0);"
    out.println "            this.historyGridControl.Name = \"historyGridControl\";"
    out.println "            this.historyGridControl.Size = new System.Drawing.Size(461, 299);"
    out.println "            this.historyGridControl.TabIndex = 0;"
    out.println "            //"
    out.println "            // panelFooter"
    out.println "            //"
    out.println "            this.panelFooter.Controls.Add(this.lableLine);"
    out.println "            this.panelFooter.Controls.Add(this.btnNext);"
    out.println "            this.panelFooter.Controls.Add(this.btnPrev);"
    out.println "            this.panelFooter.Controls.Add(this.btnClose);"
    out.println "            this.panelFooter.Dock = System.Windows.Forms.DockStyle.Bottom;"
    out.println "            this.panelFooter.Location = new System.Drawing.Point(0, 332);"
    out.println "            this.panelFooter.Name = \"panelFooter\";"
    out.println "            this.panelFooter.Size = new System.Drawing.Size(461, 60);"
    out.println "            this.panelFooter.TabIndex = 1;"
    out.println "            //"
    out.println "            // lableLine"
    out.println "            //"
    out.println "            this.lableLine.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)"
    out.println "            | System.Windows.Forms.AnchorStyles.Right)));"
    out.println "            this.lableLine.AutoSizeMode = DevExpress.XtraEditors.LabelAutoSizeMode.None;"
    out.println "            this.lableLine.LineLocation = DevExpress.XtraEditors.LineLocation.Center;"
    out.println "            this.lableLine.LineOrientation = DevExpress.XtraEditors.LabelLineOrientation.Horizontal;"
    out.println "            this.lableLine.LineVisible = true;"
    out.println "            this.lableLine.Location = new System.Drawing.Point(0, 2);"
    out.println "            this.lableLine.Name = \"lableLine\";"
    out.println "            this.lableLine.Size = new System.Drawing.Size(461, 10);"
    out.println "            this.lableLine.TabIndex = 0;"
    out.println "            //"
    out.println "            // btnNext"
    out.println "            //"
    out.println "            this.btnNext.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));"
    out.println "            this.btnNext.Enabled = false;"
    out.println "            this.btnNext.Location = new System.Drawing.Point(98, 18);"
    out.println "            this.btnNext.Name = \"btnNext\";"
    out.println "            this.btnNext.Size = new System.Drawing.Size(80, 30);"
    out.println "            this.btnNext.TabIndex = 3;"
    out.println "            this.btnNext.Text = \"下一条(&N)\";"
    out.println "            this.btnNext.Click += new System.EventHandler(this.BtnNext_Click);"
    out.println "            //"
    out.println "            // btnPrev"
    out.println "            //"
    out.println "            this.btnPrev.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));"
    out.println "            this.btnPrev.Enabled = false;"
    out.println "            this.btnPrev.Location = new System.Drawing.Point(12, 18);"
    out.println "            this.btnPrev.Name = \"btnPrev\";"
    out.println "            this.btnPrev.Size = new System.Drawing.Size(80, 30);"
    out.println "            this.btnPrev.TabIndex = 2;"
    out.println "            this.btnPrev.Text = \"上一条(&P)\";"
    out.println "            this.btnPrev.Click += new System.EventHandler(this.BtnPrev_Click);"
    out.println "            //"
    out.println "            // btnClose"
    out.println "            //"
    out.println "            this.btnClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));"
    out.println "            this.btnClose.DialogResult = System.Windows.Forms.DialogResult.Cancel;"
    out.println "            this.btnClose.Location = new System.Drawing.Point(369, 18);"
    out.println "            this.btnClose.Name = \"btnClose\";"
    out.println "            this.btnClose.Size = new System.Drawing.Size(80, 30);"
    out.println "            this.btnClose.TabIndex = 1;"
    out.println "            this.btnClose.Text = \"关闭(&C)\";"
    out.println "            this.btnClose.Click += new System.EventHandler(this.BtnClose_Click);"
    out.println "            //"
    out.println "            // Frm${t.className}Details"
    out.println "            //"
    out.println "            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 19F);"
    out.println "            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;"
    out.println "            this.ClientSize = new System.Drawing.Size(461, 392);"
    out.println "            this.Controls.Add(this.tabPane1);"
    out.println "            this.Controls.Add(this.panelFooter);"
    out.println "            this.MaximizeBox = false;"
    out.println "            this.MinimizeBox = false;"
    out.println "            this.MinimumSize = new System.Drawing.Size(450, 400);"
    out.println "            this.Name = \"Frm${t.className}Details\";"
    out.println "            this.ShowIcon = false;"
    out.println "            this.ShowInTaskbar = false;"
    out.println "            this.Text = \"${t.comment}详情\";"
    out.println "            this.Load += new System.EventHandler(this.Frm${t.className}Details_Load);"
    out.println "            this.panelFooter.ResumeLayout(false);"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.tabPane1)).EndInit();"
    out.println "            this.tabPane1.ResumeLayout(false);"
    out.println "            this.tabGeneral.ResumeLayout(false);"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.layoutPanel)).EndInit();"
    out.println "            this.layoutPanel.ResumeLayout(false);"
    out.println "            ((System.ComponentModel.ISupportInitialize)(this.layoutControlGroup1)).EndInit();"
    t.fields.each() {
        out.println "            ((System.ComponentModel.ISupportInitialize)(this.edit${it.csharpName}.Properties)).EndInit();"
        out.println "            ((System.ComponentModel.ISupportInitialize)(this.layoutItem${it.csharpName})).EndInit();"
    }
    out.println "            this.tabHistory.ResumeLayout(false);"
    out.println "            this.ResumeLayout(false);"
    out.println ""
    out.println "        }"
    out.println ""
    out.println "        #endregion"
    out.println ""
    out.println "        private DevExpress.XtraBars.Navigation.TabPane tabPane1;"
    out.println "        private DevExpress.XtraBars.Navigation.TabNavigationPage tabGeneral;"
    out.println "        private DevExpress.XtraBars.Navigation.TabNavigationPage tabHistory;"
    out.println "        private DevExpress.XtraLayout.LayoutControl layoutPanel;"
    out.println "        private DevExpress.XtraLayout.LayoutControlGroup layoutControlGroup1;"
    t.fields.each() {
        out.println "        private DevExpress.XtraEditors.TextEdit edit${it.csharpName};"
        out.println "        private DevExpress.XtraLayout.LayoutControlItem layoutItem${it.csharpName};"
    }
    out.println "        private XCI.Sys.Controls.RecordHistoryGridUserControl historyGridControl;"
    out.println "        private System.Windows.Forms.Panel panelFooter;"
    out.println "        private DevExpress.XtraEditors.LabelControl lableLine;"
    out.println "        private DevExpress.XtraEditors.SimpleButton btnPrev;"
    out.println "        private DevExpress.XtraEditors.SimpleButton btnNext;"
    out.println "        private DevExpress.XtraEditors.SimpleButton btnClose;"
    out.println "    }"
    out.println "}"
}

//#endregion

//#region 数据模型

/**
 * 表模型
 */
class DbTable {
    public String name //表名
    public String className //类名
    public String comment //注释
    public String key //主键字段
    public String keyName //主键名称
    public List<DbField> fields //字段集合
    public DasTable dasTable //表对象
}

/**
 * 字段模型
 */
class DbField {
    public String name //字段名称
    public String javaName //java字段名称
    public String csharpName //c#字段名称
    public String type //数据类型
    public String comment // 备注
    public boolean nullable // 是否可空
    public int size //字段长度
    public int scale //数字类型:整数长度
    public int precision //数字类型:小数长度
    public boolean isKey //是否主键
    public DasColumn dasColumn //列对象
}

//#endregion

//#region 公共函数

/**
 * 获取表模型
 * @param table 数据表对象
 */
def getDbTable(DasTable table) {
    DbTable t = new DbTable()
    t.name = table.getName()
    t.className = objectName(table.getName(), true)
    t.comment = table.getComment()
    if(isNotEmpty(t.comment)){
        t.comment = t.comment.replace("\r", "").replace("\n", "").replace("\r\n", "")
    }
    t.fields = getDbFields(table)
    t.dasTable = table
    t.key = t.keyName = "id"
    for (DbField field in t.fields) {
        if (!field.isKey) {
            continue;
        }
        t.key = field.name
        t.keyName = objectName(field.name, false)
    }
    return t
}

/**
 * 获取表字段集合
 * @param table 数据表对象
 */
def getDbFields(DasTable table) {
    List<DbField> list = new ArrayList<DbField>()
    DasUtil.getColumns(table).reduce([]) { fields, col ->
        def spec = Case.LOWER.apply(col.getDataType().getSpecification())
        def typeStr = typeMapping.find { p, t -> p.matcher(spec).find() }.value
        def name = col.getName()

        DbField model = new DbField()
        model.name = name
        model.javaName = objectName(name, false)
        model.csharpName = objectName(name, true)
        model.type = typeStr
        model.comment = col.comment
        if(isNotEmpty(model.comment)){
            model.comment = model.comment.replace("\r", "").replace("\n", "").replace("\r\n", "")
        }
        model.nullable = !col.isNotNull()
        model.size = col.getDataType().getLength()
        model.scale = col.getDataType().getScale()
        model.precision = col.getDataType().getPrecision()
        model.isKey = DasUtil.isPrimary(col)
        model.dasColumn = col
        list.add(model)
    }
    return list
}

/**
 * 把字段转为对象名称
 * @param str 字段名称
 * @param capitalize 首字母是否大写
 */
def objectName(str, capitalize) {
    def s = NameUtil.splitNameIntoWords(str)
            .collect { Case.LOWER.apply(it).capitalize() }
            .join("")
            .replaceAll(/[^\p{javaJavaIdentifierPart}[_]]/, "_")
    capitalize || s.length() == 1 ? s : Case.LOWER.apply(s[0]) + s[1..-1]
}

/**
 * 对象/字符串是否为空
 * @param content 对象/字符串
 * @return
 */
def isNotEmpty(Object obj) {
    return obj != null && obj.toString().trim().length() > 0
}

/**
 * 获取Model serialVersionUID代码
 */
def getSerialId() {
    return "\tprivate static final long serialVersionUID = 1L;"
}

/**
 * 是否Id列
 * @param field 字段对象
 * @return 如果是Id列返回True
 */
def isIdColumn(DbField field) {
    return field.isKey || field.javaName.endsWith("Id") || field.javaName.endsWith("id")
}

def getClientModelFieldType(DbField field) {
    String type = getModelFieldType(field)
    String result
    if (type == "Long") {
        result = "long"
    } else if (type == "Integer") {
        result = "int"
    } else if (type == "Boolean") {
        result = "bool"
    } else if (type == "Double") {
        result = "decimal"
    } else if (type == "Date") {
        result = "DateTime"
    } else {
        result = "string"
    }
    if (!field.dasColumn.isNotNull() && isClientPrimitiveType(result)) {
        return result + "?"
    }
    return result
}

def getClientFilterFieldType(DbField field) {
    String result = getClientModelFieldType(field)
    if (isClientPrimitiveType(result)&& isNotEmpty(result) && !result.contains("?")) {
        return result + "?"
    }
    return result
}

def isClientPrimitiveType(String type) {
    if (type == "long" || type == "int" || type == "bool" || type == "decimal" || type == "DateTime") {
        return true
    }
    return false
}

def getModelFieldType(DbField field) {
    if (isIdColumn(field)) {
        return "String"
    } else if (field.type == "Double" && field.precision == 0) {
        return "Integer"
    }
    return field.type
//    return field.dasColumn.getDataType().getSpecification()
}

def getExcelReplace(String comment) {
    //"启用_true" , "禁用_false"
    //状态 [启用_1, 禁用_0]
    int startIndex = comment.indexOf("[")
    int endIndex = comment.indexOf("]")
    if (startIndex < 0 || endIndex < 0) return ""
    String content = comment.substring(startIndex + 1, endIndex)
    String[] sz = content.split(",")
    if (sz == null || sz.length == 0) return ""
    StringBuilder sb = new StringBuilder()
    for (int i = 0; i < sz.length; i++) {
        String item = sz[i]
        if (item==null || item.length()==0) continue;
        sb.append("\"" + item.trim() + "\"")
        if (i != sz.length - 1) {
            sb.append(", ")
        }
    }
    return sb.toString();
}

def hasColumnOptions(String comment) {
    if (isNotEmpty(comment)){
        return comment.contains("[") && comment.contains("]") && comment.contains("_")
    }
    return false
}

def clearColumnOptions(String comment) {
    if (hasColumnOptions(comment)) {
        int startIndex = comment.indexOf("[")
        if (startIndex < 0) return comment
        return comment.substring(0, startIndex).trim()
    }
    return comment
}
//#endregion